int func(char* str)
{
char buffer[100];
unsigned short len = strlen(str);
if(len >= 100)
{
return (-1);
}
strncpy(buffer,str,strlen(str));
return 0;
}
Kode ini rentan terhadap serangan buffer overflow, dan saya mencoba mencari tahu alasannya. Saya pikir ini ada hubungannya dengan len
dinyatakan sebagai short
pengganti int
, tapi saya tidak begitu yakin.
Ada ide?
c
security
buffer-overflow
Jason
sumber
sumber
strncpy
. Dalam hal ini tidak.strlen
dihitung, digunakan untuk pemeriksaan validitas, dan kemudian dihitung secara absurd lagi - ini merupakan kegagalan KERING. Jika yang keduastrlen(str)
diganti denganlen
, tidak akan ada kemungkinan buffer overflow, terlepas dari jenislen
. Jawabannya tidak membahas poin ini, mereka hanya berhasil menghindarinya.Jawaban:
Pada kebanyakan penyusun nilai maksimum suatu
unsigned short
adalah 65535.Nilai apa pun di atas yang dililit, jadi 65536 menjadi 0, dan 65600 menjadi 65.
Ini berarti bahwa string panjang dengan panjang yang tepat (misalnya 65600) akan melewati pemeriksaan, dan meluap buffer.
Gunakan
size_t
untuk menyimpan hasilstrlen()
, bukanunsigned short
, dan membandingkanlen
dengan ekspresi yang secara langsung menyandikan ukuranbuffer
. Jadi misalnya:sumber
len
sebagai argumen ketiga strncpy. Menggunakan strlen lagi bodoh dalam hal apapun./ sizeof(buffer[0])
- perhatikan bahwasizeof(char)
di C selalu 1 (bahkan jika char berisi banyak trilyun bit) jadi itu berlebihan ketika tidak ada kemungkinan menggunakan tipe data yang berbeda. Tetap ... pujian untuk jawaban yang lengkap (dan terima kasih sudah responsif terhadap komentar).char[]
danchar*
bukan hal yang sama. Ada banyak situasi di mana achar[]
akan secara implisit dikonversi menjadichar*
. Misalnya,char[]
persis sama denganchar*
ketika digunakan sebagai tipe untuk argumen fungsi. Namun, konversi tidak terjadisizeof()
.buffer
pada suatu titik, ekspresi secara otomatis akan diperbarui. Ini sangat penting untuk keamanan, karena deklarasibuffer
mungkin beberapa baris jauh dari pemeriksaan kode aktual. Jadi mudah untuk mengubah ukuran buffer, tetapi lupa memperbarui di setiap lokasi di mana ukuran digunakan.Masalahnya ada di sini:
Jika string lebih besar dari panjang buffer target, strncpy akan tetap menyalinnya. Anda mendasarkan jumlah karakter string sebagai angka yang akan disalin, bukan ukuran buffer. Cara yang benar untuk melakukan ini adalah sebagai berikut:
Apa yang dilakukan adalah membatasi jumlah data yang disalin ke ukuran aktual buffer dikurangi satu untuk karakter terminasi nol. Kemudian kita mengatur byte terakhir dalam buffer ke karakter nol sebagai perlindungan tambahan. Alasan untuk ini adalah karena strncpy akan menyalin hingga n byte, termasuk null terminating, jika strlen (str) <len - 1. Jika tidak, maka null tidak disalin dan Anda memiliki skenario macet karena sekarang buffer Anda memiliki underminated tali.
Semoga ini membantu.
EDIT: Setelah pemeriksaan lebih lanjut dan masukan dari orang lain, kemungkinan pengkodean untuk fungsi ini adalah sebagai berikut:
Karena kita sudah mengetahui panjang string, kita bisa menggunakan memcpy untuk menyalin string dari lokasi yang dirujuk oleh str ke buffer. Perhatikan bahwa per halaman manual untuk strlen (3) (pada sistem FreeBSD 9.3), berikut ini dinyatakan:
Yang saya menafsirkan bahwa panjang string tidak termasuk nol. Itu sebabnya saya menyalin len +1 byte untuk memasukkan nol, dan tes memeriksa untuk memastikan bahwa panjang <ukuran buffer - 2. Minus satu karena buffer dimulai pada posisi 0, dan minus satu lagi untuk memastikan ada ruang untuk nol.
EDIT: Ternyata, ukuran sesuatu dimulai dengan 1 sementara akses dimulai dengan 0, jadi -2 sebelumnya salah karena akan mengembalikan kesalahan untuk apa pun> 98 byte tetapi seharusnya> 99 byte.
EDIT: Meskipun jawaban tentang unsigned short umumnya benar karena panjang maksimum yang dapat diwakili adalah 65.535 karakter, itu tidak terlalu penting karena jika string lebih panjang dari itu, nilainya akan membungkus. Ini seperti mengambil 75.231 (yang merupakan 0x000125DF) dan menutupi 16 bit teratas memberi Anda 9695 (0x000025DD). Satu-satunya masalah yang saya lihat dengan ini adalah 100 karakter pertama melewati 65.535 karena pemeriksaan panjang akan memperbolehkan penyalinan, tetapi hanya akan menyalin hingga 100 karakter pertama dari string dalam semua kasus dan null mengakhiri string . Begitu pun dengan masalah sampulnya, buffer masih tidak akan meluap.
Ini mungkin atau tidak dengan sendirinya menimbulkan risiko keamanan tergantung pada konten string dan untuk apa Anda menggunakannya. Jika hanya teks lurus yang dapat dibaca manusia, maka umumnya tidak ada masalah. Anda hanya mendapatkan string terpotong. Namun, jika itu sesuatu seperti URL atau bahkan urutan perintah SQL, Anda bisa memiliki masalah.
sumber
func
... dan setiap fungsi C lainnya yang pernah ditulis yang menggunakan string yang diakhiri NUL sebagai argumen. Memunculkan kemungkinan input yang tidak diakhiri NUL sama sekali tidak mengerti.len >= 100
) dilakukan terhadap satu nilai tetapi panjang salinan diberikan nilai yang berbeda ... ini merupakan pelanggaran terhadap prinsip KERING. Cukup meneleponstrncpy(buffer, str, len)
menghindari kemungkinan buffer overflow, dan bekerja lebih sedikit daripadastrncpy(buffer,str,sizeof(buffer) - 1)
... meskipun di sini hanya setara dengan lebih lambatmemcpy(buffer, str, len)
.Meskipun Anda menggunakan
strncpy
, panjang cutoff masih tergantung pada pointer string yang lewat. Anda tidak tahu berapa lama string itu (lokasi dari terminator nol relatif terhadap pointer, yaitu). Jadi meneleponstrlen
seorang diri membuat Anda rentan. Jika Anda ingin lebih aman, gunakanstrnlen(str, 100)
.Kode lengkap yang diperbaiki adalah:
sumber
strlen
kemudian akses melewati akhir buffer?strnlen
tidak menyelesaikan masalah jika apa yang disarankan orlp seharusnya benar.buffer
. "Karena str dapat menunjuk ke buffer 2 byte, yang keduanya tidak NUL." - itu tidak relevan, karena itu benar dari setiap pelaksanaanfunc
. Pertanyaannya di sini adalah tentang buffer overflow, bukan UB karena inputnya tidak diakhiri dengan NUL.Jawaban dengan pembungkusnya benar. Tapi ada masalah yang saya pikir tidak disebutkan jika (len> = 100)
Jika Len 100, kita akan menyalin 100 elemen dan kita tidak akan tertinggal \ 0. Itu jelas akan berarti fungsi lain tergantung pada string akhir yang tepat akan berjalan jauh melampaui array asli.
String yang bermasalah dari C adalah IMHO yang tidak dapat diselesaikan. Anda sebaiknya memiliki beberapa batasan sebelum panggilan, tetapi itu pun tidak akan membantu. Tidak ada batas memeriksa dan buffer overflows selalu dapat dan sayangnya akan terjadi ....
sumber
strncpy()
dan teman, tapi fungsi pengalokasian memori sukastrdup()
dan teman. Mereka berada dalam standar POSIX-2008, sehingga mereka cukup portabel, meskipun tidak tersedia pada beberapa sistem berpemilik.buffer
bersifat lokal untuk fungsi ini dan tidak digunakan di tempat lain. Dalam sebuah program nyata kita harus memeriksa bagaimana ini digunakan ... kadang-kadang tidak mengakhiri NUL benar (penggunaan asli strncpy adalah untuk membuat entri direktori 14 byte UNIX - NUL-empuk dan bukan NUL-dihentikan). "String yang bermasalah dari C adalah IMHO yang tidak dapat diselesaikan" - sementara C adalah bahasa yang sulit dipahami yang telah dilampaui oleh teknologi yang jauh lebih baik, kode aman dapat ditulis di dalamnya jika cukup disiplin digunakan.if (len >= 100)
adalah kondisi ketika cek gagal , bukan ketika melewati, yang berarti tidak ada kasus di mana tepatnya 100 byte tanpa terminator NUL disalin, karena panjangnya termasuk dalam kondisi gagal.Di luar masalah keamanan yang terlibat dengan pemanggilan
strlen
lebih dari satu kali, seseorang umumnya tidak boleh menggunakan metode string pada string yang panjangnya diketahui dengan tepat [untuk sebagian besar fungsi string, hanya ada kasus yang sangat sempit di mana mereka harus digunakan - pada string yang maksimum panjang bisa dijamin, tetapi panjang pastinya tidak diketahui]. Setelah panjang string input diketahui dan panjang buffer output diketahui, orang harus mencari tahu seberapa besar suatu wilayah harus disalin dan kemudian digunakanmemcpy()
untuk benar-benar melakukan salinan yang dimaksud. Meskipun mungkin yangstrcpy
mungkin mengunggulimemcpy()
ketika menyalin string hanya 1-3 byte atau lebih, pada banyak platformmemcpy()
cenderung lebih dari dua kali lebih cepat ketika berhadapan dengan string yang lebih besar.Meskipun ada beberapa situasi di mana keamanan akan mengorbankan kinerja, ini adalah situasi di mana pendekatan yang aman juga lebih cepat. Dalam beberapa kasus, mungkin masuk akal untuk menulis kode yang tidak aman terhadap input berperilaku aneh, jika kode yang memasok input dapat memastikan mereka akan berperilaku baik, dan jika menjaga terhadap input berperilaku buruk akan menghambat kinerja. Memastikan bahwa panjang string hanya diperiksa sekali meningkatkan baik kinerja dan keamanan, meskipun satu hal tambahan yang dapat dilakukan untuk membantu penjaga keamanan bahkan ketika pelacakan panjang string secara manual: untuk setiap string yang yang diharapkan memiliki nol miring, menulis nol Trailing eksplisit lebih daripada mengharapkan string sumber untuk memilikinya. Jadi, jika seseorang menulis yang
strdup
setara:Perhatikan bahwa pernyataan terakhir secara umum dapat dihilangkan jika memcpy telah memproses
len+1
byte, tetapi utas lainnya adalah untuk memodifikasi string sumber, hasilnya bisa menjadi string tujuan yang tidak diakhiri dengan NUL.sumber
strlen
lebih dari satu kali ?strlen
dan mengambil tindakan berdasarkan nilai yang dikembalikan (yang mungkin merupakan alasan untuk memanggilnya di tempat pertama), maka panggilan berulang baik (1) akan selalu menghasilkan jawaban yang sama seperti yang pertama, dalam hal ini hanya buang-buang pekerjaan, atau (2) kadang-kadang (karena sesuatu yang lain - mungkin utas lainnya - memodifikasi string sementara itu) menghasilkan jawaban yang berbeda, di mana kode kasus yang melakukan beberapa hal dengan panjang (misalnya mengalokasikan buffer) dapat mengasumsikan ukuran yang berbeda dari kode yang melakukan hal-hal lain (menyalin ke buffer).