Snprintf () HER ZAMAN boş sonlandırıyor mu?


85

Snprintf her zaman hedef tamponu null mu sonlandırıyor?

Başka bir deyişle, bu yeterli mi:

yoksa eğer yeterince uzunsa, böyle yapmak zorunda mısın?

Hem standardın ne söylediği hem de bazı popüler libc'lerin standart davranış olmayan ne yapabileceği ile ilgileniyorum.


İkinci örnekte somestr veya dst'yi geçersiz kılmak mı istiyorsunuz?
Hudson

@chux, Martin Ba bunu kabul edilen cevapta kapladı. :)
Prof. Falken

@chux İyi olduğunu düşünüyorum, yorumunuz çok açık bir şekilde dest i 0 uzunsa hiçbir şey yazılmaz. Her yorumu, diğer stackoverflowers ile sohbet etmek için potansiyel bir bahane olarak alıyorum. :)
Prof. Falken

@Prof. Falken Bu yorumun iyi ve açık olduğunu kabul ediyorum, ancak cevaplarda gereksizdi - incelememde bunu kaçırdım.
chux - Monica'yı yeniden etkinleştir

stackoverflow.com/a/8712996/193892 Visual Studio artık snprintf () 'i
Prof. Falken

Yanıtlar:


73

Diğer cevaplar kurmak gibi: Bu should :

snprintf... Sonuçları bir karakter dizisi arabelleğine yazar. (...) buf_size sıfır olmadığı sürece boş bir karakterle sonlandırılacaktır.

Öyleyse dikkat etmeniz gereken tek şey, ona sıfır boyutlu bir tampon geçirmemenizdir, çünkü (açıkçası) "hiçbir yere" sıfır yazamaz.


Ancak dikkat Microsoft'un kütüphane olduğunu bulunmamaktadır adlı bir işlev snprintfyerine tarihsel olarak sadece adında bir işlevi vardı _snprintf(Not lider çizgi) append değil bir sonlandırma null adlı. İşte belgeler (VS 2012, ~~ VS 2013):

http://msdn.microsoft.com/en-us/library/2ts7cx93%28v=vs.110%29.aspx

Geri dönüş değeri

L, biçimlendirilmiş veri dizgisinin uzunluğu olsun (sonlandırıcı boş dahil değil). len ve count, _snprintf için bayt cinsindendir, _snwprintf için geniş karakterler.

  • Len <count ise, len karakterleri arabellekte saklanır, boş sonlandırıcı eklenir ve len döndürülür.

  • Len = count ise, len karakterleri arabellekte saklanır, boş sonlandırıcı eklenmez ve len döndürülür.

  • Len> count ise, o zaman count karakterleri tamponda saklanır, boş sonlandırıcı eklenmez ve negatif bir değer döndürülür.

(...)

Visual Studio 2015 (VC14) görünüşte uyumlu snprintfişlevi tanıttı , ancak önde gelen alt çizgi ve boş sonlandırıcı olmayan davranışa sahip eski işlev hala var:

snprintfLen daha büyük olan ya da bir boşlukla sonlandırıcı yerleştirerek, saymak eşit olduğunda fonksiyon çıkışını keser buffer[count-1]. (...)

Tüm fonksiyonları için diğer daha snprintfuzunluk = sayısı ise, len karakter tamponunda depolanır, bir NULL-terminatör eklenir , (...)


24
Microsoft mühendisleri , önemli bir güvenlik özelliğini_snprintf sessizce ortadan kaldıransnprintf ve dizenin boş sonlandırılmamasına izin veren tanıttığında Aslan adına ne düşünüyorlardı ?!
Colin D Bennett

2
@ColinDBennett - bu tuhaf ve çok can sıkıcı ve kimse düşündüyse hiçbir fikrim yok :-)
Martin Ba

2
@MartinBa evet üzgünüm, test ettiğim şeydi template <size_t size> int _snprintf_s(char (&buffer)[size], size_t count, const char *format [, argument] ...);ve bunun sadece / GS (Güvenlik Kontrolü) derleme bayrağıyla gerçekleştiğini de belirtmeliyim. Bu işlev boyutu, sayısı ve uzunluğu bilir.
sekmet64

3
Aksi belirtilmediği sürece mingw64'ün microsoft _snprintf uygulamasını "normal" snprintf olarak kullandığına (kullandığına) dikkat edin nvd.nist.gov/vuln/detail/CVE-2018-1000101
domenukk

2
@Sajjon Bu, belki de biraz kıyılmış bir yemin ( en.wikipedia.org/wiki/Minced_oath ) gibi öfkenin ( deyimler . Başka bir örnek de "Zeus adına ne ...?!" ( forum.wordreference.com/threads/in-the-name-of-zeus.2132965 )
Colin D Bennett

19

Snprintf (3) kılavuz sayfasına göre.

İşlevler snprintf()ve vsnprintf()en çok sizebayt yazılır (sondaki boş bayt ('\ 0') dahil) str.

Yani, evet, eğer boyut> = 1 ise sonlandırmaya gerek yok.


3
Ve bunun için tanrıya şükür; bu tek mantıklı tasarımdır. Bu işlevlerin kontrol edilen sürümlerinin tüm amacı güvenli olmaktır ve tüm sonlandırma malarkeyini elle yapmanız gerekse çok kötü olur.
Kerrek SB

1
Buna güvenmeden önce kullandığınız platformlarda test etmenizi tavsiye ederim. O bile gereken boş bayt yazma, ben yapmadım uygulamaları çalıştırmak biliyorum (eski bir MS çalışma zamanı kullanılan MinGW ile olmuş olabilir).
Dmitri

10

C standardına göre, tampon boyutu 0 olmadıkça vsnprintf()ve snprintf()null çıktısını sonlandırmaz.

snprintf()İşlev eşdeğer olacaktır sprintf()tampon boyutu s ile ifade belirtmektedir, n bağımsız değişken eklenmesi ile. N sıfırsa, hiçbir şey yazılmaz ve s bir boş gösterici olabilir. Aksi takdirde, n-1'in ötesindeki çıktı baytları diziye yazılmak yerine atılır ve diziye fiilen yazılan baytların sonuna boş bir bayt yazılır.

Öyleyse, bir arabelleğin ne kadar büyük olduğunu bilmeniz gerekiyorsa, sıfır boyutunu kullanın ve ardından hedef olarak bir boş gösterici kullanabilirsiniz. POSIX sayfalarına bağlantı verdiğime dikkat edin, ancak bunlar açıkça Standart C ve POSIX arasında aynı zemini kapsadıklarında herhangi bir sapma olmaması gerektiğini söylüyor:

Bu referans sayfasında açıklanan işlevsellik, ISO C standardıyla uyumludur. Burada açıklanan gereksinimler ile ISO C standardı arasındaki herhangi bir çelişki kasıtsızdır. POSIX.1-2008'in bu hacmi, ISO C standardına göre değişir.

Microsoft sürümüne karşı dikkatli olun vsnprintf(). Arabellekte yeterli alan olmadığında kesinlikle standart C sürümünden farklı davranır (standart işlevin gerekli uzunluğu döndürdüğü durumlarda -1 döndürür). Microsoft sürümünün null çıktısını hata koşullarında sonlandırdığı, standart C sürümünün ise yaptığı tam olarak açık değildir.

TR 24731 güvenli fonksiyonlarını kullanıyor musunuz? Sorusunun yanıtlarına da dikkat edin. ( Microsoft sürümü için MSDN'ye bakın vsprintf_s()) ve güvenli olmayan C standart kitaplık işlevlerine güvenli alternatifler için Mac çözümü?


oh, kötü, bunu hiç düşünmedim. Öte yandan ... :)
Prof. Falken

ah, sanırım MS vsprintf () beni ısırdı ve bu - 1 alışkanlık
Prof. Falken

4

SunOS'un bazı eski sürümleri snprintf ile tuhaf şeyler yaptılar ve çıktıyı BOŞTA sonlandırmamış olabilir ve diğer herkesin yaptığıyla eşleşmeyen dönüş değerlerine sahip olabilir, ancak son 10 yılda piyasaya sürülen her şey C99'u yapıyordu. diyor.


XP'nin 10 yıldan biraz daha uzun bir süre önce yayınlandığını fark ettim. :-)
Prof. Falken

Ve bu yıl kullanımdan kaldırıldı. :)
Prof. Falken

4

Belirsizlik C Standardının kendisinden başlar. Hem C99 hem de C11 aynı snprintfişlev açıklamasına sahiptir . İşte C99'daki açıklama:

7.19.6.5 snprintfİşlev
Özet
1 #include <stdio.h> int snprintf(char * restrict s, size_t n, const char * restrict format, ...);
Açıklama
2 snprintfİşlev, fprintfçıktının sbir akış yerine bir diziye (bağımsız değişken tarafından belirtilen ) yazılması dışında işlevine eşdeğerdir . Eğer nis sıfır, hiçbir şey yazılır ve sbir boş gösterici olabilir. Aksi takdirde, n-1dizinin ötesindeki çıktı karakterleri diziye yazılmak yerine atılır ve diziye gerçekte yazılan karakterlerin sonuna bir boş karakter yazılır. Kopyalama çakışan nesneler arasında gerçekleşirse, davranış tanımsızdır. 3
döndürür İşlev, önceden yazılmış olması gereken karakter sayısını döndürür.
snprintfnyeterince büyük, sonlandırıcı boş karakter sayılmıyor veya bir kodlama hatası oluştuysa negatif bir değer. Böylece, boş sonlandırılmış çıktı, ancak ve ancak döndürülen değer negatif değilse ve değerinden küçükse tamamen yazılmıştır n.

Bir yandan cümle

Aksi takdirde, ötesinde çıkış karakterleri n-1st atılır diziye yazılırken ziyade, bir null karakteri aslında yazılı karakterlere sonunda yazılır diziye

diyor
(eğer s3 karakter uzunluğunda diziye noktaları ve) n3, daha sonra 2 karakter yazılır ve 2 tek ötesinde karakterler atılır ; daha sonra bu 2'den sonra boş karakter yazılır (ve boş karakter 3. karakter yazılır) .

Ve bunun asıl soruyu yanıtladığını düşünüyorum.
CEVAP:
Kopyalama çakışan nesneler arasında gerçekleşirse, davranış tanımsızdır.
Eğer n0 sonra hiçbir şey çıktıya yazılır olan
hiçbir kodlama hataları karşılaştı, aksi takdirde, çıkışı DAİMA boş sonlandırılmış ( ne olursa olsun ister çıkış dizi veya değil çıktı uyuyor ; bazı karakterler çıktı böyle atılır eğer yapmazsan dizi asla aşılmaz),
aksi takdirde (kodlama hatalarıyla karşılaşılırsa) çıktı boş sonlandırılmamış kalabilir .

Öte yandan
son cümle

Bu nedenle, boş sonlandırılmış çıktı, yalnızca ve ancak döndürülen değer negatif değilse ve şundan küçükse tamamen yazılmıştır. n

belirsizlik veriyor (veya İngilizcem yeterince iyi değil). En az iki yolla cümle yorumlayabilir:
1. çıkış boş sonlandırılmış ve eğer geri değeri negatif olmayan ve yalnızca daha azn geri değer olması durumunda olan aracı ( değil az n, örneğin, çıkış (dahil null karakterini sonlandırmak) diziye sığmazsa, çıktı boş olarak sonlandırılmaz ).
2. Çıktı tamamlanır (hiçbir karakter atılmamıştır) ancak ve ancak döndürülen değer negatif değilse ve değerinden küçüksen .


Yukarıdaki yorumun 1 CEVAPLA çeliştiğine, yanlış anlaşılmalara ve uzun tartışmalara neden olduğuna inanıyorum. Bu nedenle, snprintfherhangi bir belirsizliği ortadan kaldırmak için işlevi tanımlayan son cümlenin bir değişikliğe ihtiyacı vardır (bu, C dili Standardına bir Teklif yazmak için zemin sağlar).
İnanıyorum ki belirsiz olmayan ifade örneği http://en.cppreference.com/w/c/io/fprintf (bkz. 4)) Adresinden, bağlantı için @ "Martin Ba" sayesinde alınabilir .

Ayrıca " snprintf: Bu işlevin açıklamasını değiştirmek için herhangi bir C Standart Önerisi / planı var mı? " Sorusuna da bakın .


4
Yorumunuz 1 bana hiç mantıklı gelmiyor. Bu cümleyi "Çıktı (ki tesadüfen boş sonlandırılmış olan) tamamen yazılmıştır eğer ..." olarak ayrıştırıyorum ki bunu sadece # 2 olarak anlayabiliyorum.
zwol

1
Cümle olumsuzlaması "boş sonlandırılmış çıkış vardır edilir "boş sonlandırılmış çıktı tamamen yazılmıştır" değil tamamen yazılmıştır". Daha fazlası yok. Olumsuzlanan cümle kendi başına herhangi bir şeyin yazıldığı anlamına gelmez (bu, eksik boş sonlandırılmış çıktı, tamamlanmamış, boş olmayan sonlandırılmış çıktı veya renksiz yeşil fikirleri içerir). Standarttaki başka bir yer, çıktı eksik olduğunda tam olarak ne yazıldığını söyler ve bu yer , boş olmadığı sürece çıktının boş sonlandırıldığını belirtir (n == 0).
n. zamirler 'm.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.