Neden gets()
tehlikeli
İlk internet solucanı ( Morris İnternet Solucanı ) yaklaşık 30 yıl önce (1988-11-02) kaçtı ve gets()
sistemden sisteme yayılma yöntemlerinden biri olarak bir tampon taşması kullandı . Temel problem, fonksiyonun tamponun ne kadar büyük olduğunu bilmemesidir, bu nedenle yeni bir satır bulana veya EOF ile karşılaşana kadar okumaya devam eder ve verilen tamponun sınırlarını aşabilir.
Var olduğunu duyduğunuzu unutmalısınız gets()
.
C11 standardı ISO / IEC 9899: 2011 gets()
standart bir işlev olarak ortadan kaldırıldı , bu da A Good Thing ™ (resmi olarak ISO / IEC 9899: 1999 / Cor.3: 2007'de 'eskimiş' ve 'kullanımdan kaldırıldı' olarak işaretlendi) C99 için 3'tür ve daha sonra C11'de çıkarılır). Ne yazık ki, geriye dönük uyumluluk nedeniyle kütüphanelerde uzun yıllar ('on yıllar' anlamına gelir) kalacaktır. Bana kalmış gets()
olsaydı , uygulaması şöyle olurdu:
char *gets(char *buffer)
{
assert(buffer != 0);
abort();
return 0;
}
Kodunuzun zaten er ya da geç çökeceği göz önüne alındığında, sorunu daha sonra değil, daha erken halletmek daha iyidir. Bir hata mesajı eklemeye hazır olurum:
fputs("obsolete and dangerous function gets() called\n", stderr);
Linux derleme sisteminin modern sürümleri, bağlantı kurarsanız gets()
ve ayrıca güvenlik sorunları olan bazı diğer işlevler için uyarılar üretir ( mktemp()
,…).
Alternatifleri gets()
fgets ()
Herkesin dediği gibi, kanonik alternatif etmek gets()
olduğunu fgets()
belirterek stdin
dosya akışı olarak.
char buffer[BUFSIZ];
while (fgets(buffer, sizeof(buffer), stdin) != 0)
{
...process line of data...
}
Daha önce kimsenin bahsetmediği şey gets()
, yeni satırı içermediği, ancak fgets()
içermediği. Bu nedenle, fgets()
yeni satırı silen bir sarmalayıcı kullanmanız gerekebilir :
char *fgets_wrapper(char *buffer, size_t buflen, FILE *fp)
{
if (fgets(buffer, buflen, fp) != 0)
{
size_t len = strlen(buffer);
if (len > 0 && buffer[len-1] == '\n')
buffer[len-1] = '\0';
return buffer;
}
return 0;
}
Ya da daha iyisi:
char *fgets_wrapper(char *buffer, size_t buflen, FILE *fp)
{
if (fgets(buffer, buflen, fp) != 0)
{
buffer[strcspn(buffer, "\n")] = '\0';
return buffer;
}
return 0;
}
Ayrıca, caf bir yorumda belirttiği ve paxdiablo'nun cevabında gösterdiği gibi fgets()
, bir satırda kalan veriler olabilir. Sarıcı kodum bu verileri bir dahaki sefere okunacak şekilde bırakır; isterseniz, veri satırının geri kalanını silip süpürmek için kolayca değiştirebilirsiniz:
if (len > 0 && buffer[len-1] == '\n')
buffer[len-1] = '\0';
else
{
int ch;
while ((ch = getc(fp)) != EOF && ch != '\n')
;
}
Kalan sorun, üç farklı sonuç durumunun nasıl rapor edileceğidir - EOF veya hata, satır okuma ve kesilmemiş ve kısmi satır okuma ancak veriler kesildi.
Bu sorun, gets()
ara belleğinizin nerede sona erdiğini ve nihayetinde nihayetinde ezildiğini bilmediği için, güzel bir şekilde ayrılmış bellek düzeninize zarar verir, arabellek tahsis edilirse genellikle dönüş yığınını ( Yığın Taşması ) bozar. yığını dinamik olarak tahsis edilmişse kontrol bilgisi üzerinden ya da arabelleğe statik olarak tahsis edilmişse diğer değerli global (veya modül) değişkenlere veri kopyalanması. Bunların hiçbiri iyi bir fikir değildir - 'tanımsız davranış' ifadesini özetlerler.
Ayrıca aşağıdakiler de dahil olmak üzere çeşitli işlevlere daha güvenli alternatifler sunan TR 24731-1 (C Standart Komitesinden Teknik Rapor) bulunmaktadır gets()
:
§6.5.4.1 gets_s
İşlev
özet
#define __STDC_WANT_LIB_EXT1__ 1
#include <stdio.h>
char *gets_s(char *s, rsize_t n);
Süre-kısıtları
s
boş bir işaretçi olamaz. n
sıfıra eşit veya RSIZE_MAX değerinden büyük olamaz. Adlı n-1
karakterleri okurken yeni satır karakteri, dosya sonu veya okuma hatası oluşacaktır
stdin
. 25)
3 Çalışma zamanı kısıtlaması ihlali varsa, s[0]
boş karakter olarak ayarlanır ve stdin
yeni satır karakteri okunana veya dosya sonu veya okuma hatası oluşana kadar karakterler okunur ve atılır .
Açıklama
4 gets_s
İşlev n
, işaret ettiği akıştan, işaret stdin
ettiği diziye kadar belirtilen karakter sayısından en az birini daha az okur s
. Yeni bir karakterden (atılan) veya dosya sonundan sonra başka hiçbir karakter okunmaz. Atılan yeni satır karakteri okunan karakter sayısına dahil edilmez. Diziye son karakter okunduktan hemen sonra boş bir karakter yazılır.
5 Dosya sonu ile karşılaşılırsa ve diziye hiçbir karakter okunmazsa veya işlem sırasında bir okuma hatası oluşursa s[0]
, boş karakter olarak ayarlanır ve diğer öğeler s
belirtilmemiş değerleri alır.
Önerilen uygulama
6 fgets
İşlev, düzgün yazılan programların, sonuç dizisinde depolanamayacak kadar uzun giriş satırlarını güvenli bir şekilde işlemesini sağlar. Genel olarak bu, arayanların fgets
sonuç dizisinde yeni satır karakterinin varlığına veya yokluğuna dikkat etmesini gerektirir . fgets
Bunun yerine (yeni satır karakterlerine dayalı gerekli işlemlerle birlikte)
kullanmayı düşünün gets_s
.
25)gets_s
farklı olarak işlev, gets
bu saklamak bellek taşmasına girdinin bir hat için bir çalışma zamanı-kısıtlaması ihlali yapar. Bunun aksine fgets
, gets_s
giriş hatları ile başarılı çağrılar arasında bire bir ilişki sürdürür gets_s
. Kullanan programlar gets
böyle bir ilişki bekler.
Microsoft Visual Studio derleyicileri, TR 24731-1 standardına bir yaklaşım uygular, ancak Microsoft tarafından TR'de imzalanan imzalar arasında farklılıklar vardır.
C11 standardı ISO / IEC 9899-2011, kütüphanenin isteğe bağlı bir parçası olarak Ek K'da TR24731 içerir. Ne yazık ki, nadiren Unix benzeri sistemlerde uygulanır.
getline()
- POSIX
POSIX 2008 de güvenli bir alternatif sunmaktadır gets()
denir getline()
. Çizgi için dinamik olarak yer ayırır, böylece onu serbest bırakmanız gerekir. Bu nedenle, hat uzunluğu sınırlamasını kaldırır. Ayrıca okunan verinin uzunluğunu döndürür -1
( ya da değil EOF
!). Ayrıca 'kendi tek karakterli sınırlayıcınızı seçin' varyasyonu vardır getdelim()
; örneğin find -print0
, dosya adlarının uçlarının ASCII NUL '\0'
karakteriyle işaretlendiği çıktıyla ilgileniyorsanız bu yararlı olabilir .
gets()
Buffer_overflow_attack