2015-01-20 23 views
10

Birkaç platformda C++ ile snprintf uygulamasının çok garip bir davranışını (bence) fark etmedim. Aşağıdaki kodu (gözlenen davranışı neden olan asgari çalışma örneği) düşünün:Beklenmedik snprintf davranışı

#include <stdio.h> 

char test1[512]; 
char test2[512]; 
char test3[1024]; 
char test4[1024]; 

int main() 
{ 
    snprintf(test1, sizeof(test1), "test1"); 
    snprintf(test2, sizeof(test2), "test2"); 
    snprintf(test3, sizeof(test3), "%s %s", test1, test2); 
    return 0; 
} 

= exp-sgcheck, aşağıdaki hata (3 snprintf deyimi için) bildirilmektedir --tool ile valgrind olsa çalışan:

==30302== Invalid read of size 1 
==30302== at 0x568E4EB: vfprintf (in /lib64/libc-2.19.so) 
==30302== by 0x56B7608: vsnprintf (in /lib64/libc-2.19.so) 
==30302== by 0x5695209: snprintf (in /lib64/libc-2.19.so) 
==30302== by 0x4006AD: main (1.cc:12) 
==30302== Address 0x601460 expected vs actual: 
==30302== Expected: global array "test1" of size 1,024 in object with soname "NONE" 
==30302== Actual: global array "test2" of size 512 in object with soname "NONE" 
==30302== Actual: is 0 after Expected 

Test1'i ilk% s argümanı olarak geçirdikten sonra, test1 dizisinin sonuna kadar okunan bir okumaya yol açar.

Bu davranış

(evet ben ... statik verileri biliyorum) bir windows sürücüsü birkaç sayfa hataları neden oldu. Neyse ki kod taşınabilir ve linux valgrind'e taşındığında bu hata bildirildi.

Ama benim gördüğüm kadarıyla

, snprintf (öyle ki, o işaretli) 6 byte olarak \ 0 ile test1 sonlandırmak gerekir. Peki neden 3. snprintf ifadesinin test1 dizisinin sonundan sonra okunmasına neden sebep oluyor? 3. snprintf ifadesinin 'un her iki platformdaki sorununu çözmesi. Kodu C kodu (C++ değil) olarak derlemek hiç hataya neden olmaz.

GÜNCELLEME: hata yalnızca oluşur linux üzerinde (ve belki pencereler yanı), kod ayıklama bilgileri ile derlenmiş dahildir ve optimizasyonlar (gcc için -g O0) devre dışı bırakılırsa.

+1

Bu durumda bir değişiklik yapması gerekmiyor, ancak kaynak dosya adı valgde çıktısında "1.cc" olarak çağrılıyor. C yazmak ve bir C++ derleyicisi ile derlemek için bir neden var mı? – Blrfl

+1

Sadece kodunuzu "valgrind --tool = exp-sgcheck" ile test ettim ve hata bildirilmedi, hangi "valgrind" sürümünü kullanıyorsunuz ve glibc'nin hangi sürümünü kullanıyorsunuz, ayrıca Linux Distro'nuz nedir? –

+1

Bize biraz daha anlatabilir misiniz? Örneğin. platformunuz, derleyici sürümünüz, kodu nasıl derlersiniz. – nos

cevap

1

Genel nesneler (örneğinizdeki diziler gibi) 0 tarafından başlatılmış olduğundan, son snprintf, önceki sprint dosyalarının 0 karakterini sonlandırıp sonlandırmayacağından bağımsız olarak, dizenin sonunun ötesinde hiçbir zaman okumamalıdır. Tek açıklama, önceki snprintf'lerin gönderilen "test1" den çok daha fazlasını test1 hedefine kopyalaması ve tüm 0'ları 0'lı olmayanlarla yazmasıdır (0'lar rastgele bir bellekle olası değildir). çok olası değildir

- böyle bariz bir hata daha önce bulunmuş olacaktır. Sürücüdeki hata ile ilgili olarak, belleğin tamamen ilgisiz bir "süreç" (genel anlamda, belki başka bir sürücü) tarafından üzerine yazıldığından şüpheleniyorum. Bir masaüstü uygulaması için neden başarısız olacağını açıklamam. Örneğinizi gcc 4.8.3 ile Codingground'da denemek iyi çalışıyordu ve sonunda bir printf() eklediğimde beklenen dizeleri basıyordu.

Btw, orijinal kod etkin optimizasyonlarla iyi çalışır şaşırtıcı değil: derleyici sadece NOP yayabilir gözlemlenebilir etki olmadığı.

+0

Bir gün önce cevabınıza mükemmel bir şekilde uyum sağlamış olurum;) Ama şimdi, sürücünün statik veri bölümünden hemen sonra bir PAGE_FAULT ile snprintf ifadesine tam olarak işaret eden bazı çarpışmalar var. Daha önce de belirtildiği gibi, snprintf'nin değiştirilmesi bu sorunu çözdü. Ayrıca çarpışma, sürücü başlangıcından hemen sonra gerçekleşir, bu nedenle bu hatayı tetiklemek için gerekli hiçbir şey yoktu. Ancak, burada koşulların çok karmaşık olduğunu kabul ediyorum ve sonuçta ms C++ derleyicisinde bir hata gibi görünüyor. Ne yazık ki windows için bir valgrind yoktur :( – Johannes

+0

gcc 4.9.x cygwin'de ve VS 2013'te sadece iyi bir örnek çalıştırır (ama pencerelerdeki bir valgrind, bu yüzden sadece prog'u çalıştırabilirim). normal bir kullanıcı uzay programı? DÜZENLEME: C olarak derledim, orijinal yazıyı yeniden okumadım ;-) EDIT2: VS2013 C++ olarak da iyi çalışıyor. –

+0

Bu hatayı kullanıcı alanında yeniden üretemedim (yani, bir çökmeye neden olabilir). Ancak çoğu durumda, disk belleği belleğinin son bildirilen statik verileri izlemesi olası olduğundan, herhangi bir çarpışma tetiklemez. Uygun bir test vakası yapmak için, geçersiz okuma erişiminin gerçekleştiği koşullar altında ayrıntılandırmanın daha iyi olması için valgind gibi bir şeyin olması ve belleğe alınmamış belleğe nasıl yerleştirilebileceği (hizalama ve sayfa boyutu gibi şeylere saygı gösterilmesi) çok kullanışlı olacaktır. Ancak ticari bir ürün olduğu için orijinal kodu burada gösteremiyorum. – Johannes