Fungsi apa dari pustaka standar yang harus (harus) dihindari?

90

Saya telah membaca di Stack Overflow bahwa beberapa fungsi C "usang" atau "harus dihindari". Bisakah Anda memberi saya beberapa contoh fungsi semacam ini dan alasannya?

Alternatif apa untuk fungsi tersebut yang ada?

Bisakah kita menggunakannya dengan aman - ada praktik yang baik?

Andrei Ciobanu
sumber
3
Saya percaya itu adalah hal yang sangat baik untuk diketahui. Beberapa fungsi dari C harus benar-benar dihindari dan hanya digunakan untuk tujuan pendidikan.
INS
@Felix, akan lebih mudah untuk mengedit satu jawaban (ditandai benar) di masa mendatang. Itu benar-benar mengudara tentang jawaban yang kemungkinan akan berubah seiring waktu. Siapa tahu, mungkin Jeff akan memberikan lencana 'petugas kebersihan' kepada orang-orang yang terus memberikan jawaban terkini dalam beberapa tahun mendatang.
Tim Post
1
@Tim Post: Ok saya akan menghapus komentar saya.
Felix Kling
3
Saya tidak akan menggunakan strncpy()sebagai pengganti umum untuk strcpy(), dan saya tidak akan strncat()pernah menggunakannya karena memiliki antarmuka paling tidak intuitif yang bisa dibayangkan - apakah ANDA tahu apa yang ditentukan oleh parameter panjang?
Jonathan Leffler
2
Menggunakan strncpy dan strncat hampir selalu merupakan kesalahan. Mereka tentunya tidak boleh digunakan sebagai pengganti strcpy dan strcat !! scanf dan sprintf juga sangat berguna jika Anda tahu bagaimana menggunakannya ...
R .. GitHub STOP HELPING ICE

Jawaban:

58

Deprecated Functions
Insecure
Contoh sempurna dari fungsi tersebut adalah gets () , karena tidak ada cara untuk mengatakan seberapa besar buffer tujuan. Akibatnya, program apa pun yang membaca input menggunakan gets () memiliki kerentanan buffer overflow . Untuk alasan serupa, seseorang harus menggunakan strncpy () sebagai ganti strcpy () dan strncat () sebagai ganti strcat () .

Namun beberapa contoh lainnya termasuk fungsi tmpfile () dan mktemp () karena potensi masalah keamanan dengan menimpa file - file sementara dan yang digantikan oleh fungsi mkstemp () yang lebih aman .

Non-Reentrant
Contoh lain termasuk gethostbyaddr () dan gethostbyname () yang non-reentrant (dan, oleh karena itu, tidak dijamin sebagai threadsafe) dan telah digantikan oleh reentrant getaddrinfo () dan freeaddrinfo () .

Anda mungkin memperhatikan pola di sini ... baik kurangnya keamanan (mungkin karena gagal menyertakan cukup informasi dalam tanda tangan untuk menerapkannya dengan aman) atau tidak masuk kembali adalah sumber umum penghentian.

Kedaluwarsa, Tidak Portabel
Beberapa fungsi lain menjadi tidak digunakan lagi karena mereka menggandakan fungsionalitas dan tidak portabel seperti varian lainnya. Misalnya, bzero () tidak digunakan lagi untuk mendukung memset () .

Keamanan dan Masuk Kembali Thread
Anda bertanya, dalam postingan Anda, tentang keamanan dan akses masuk kembali thread. Ada sedikit perbedaan. Sebuah fungsi reentrant jika tidak menggunakan status bersama yang bisa berubah. Jadi, misalnya, jika semua informasi yang dibutuhkan diteruskan ke dalam fungsi, dan semua buffer yang diperlukan juga diteruskan ke fungsi (bukan dibagikan oleh semua panggilan ke fungsi), maka itu adalah reentrant. Itu berarti bahwa utas yang berbeda, dengan menggunakan parameter independen, tidak mengambil risiko status berbagi secara tidak sengaja. Reentrancy adalah jaminan yang lebih kuat daripada keamanan benang. Sebuah fungsi aman untuk thread jika dapat digunakan oleh beberapa thread secara bersamaan. Suatu fungsi aman untuk thread jika:

  • Ini adalah reentrant (yaitu tidak berbagi status apa pun di antara panggilan), atau:
  • Ini non-reentrant, tetapi menggunakan sinkronisasi / penguncian yang diperlukan untuk status bersama.

Secara umum, dalam Spesifikasi UNIX Tunggal dan IEEE 1003.1 (yaitu "POSIX"), fungsi apa pun yang tidak dijamin sebagai reentrant tidak dijamin aman untuk thread. Jadi, dengan kata lain, hanya fungsi yang dijamin reentrant yang dapat digunakan secara portabel dalam aplikasi multithread (tanpa penguncian eksternal). Namun, itu tidak berarti bahwa penerapan standar ini tidak dapat memilih untuk membuat fungsi non-reentrant aman untuk thread. Misalnya, Linux sering menambahkan sinkronisasi ke fungsi non-reentrant untuk menambahkan jaminan (di luar Spesifikasi UNIX Tunggal) dari threadsafety.

Strings (dan Memory Buffer, secara Umum)
Anda juga ditanya apakah ada beberapa kelemahan mendasar dengan string / array. Beberapa orang mungkin berpendapat bahwa ini masalahnya, tetapi saya berpendapat bahwa tidak, tidak ada kekurangan mendasar dalam bahasa tersebut. C dan C ++ mengharuskan Anda meneruskan panjang / kapasitas larik secara terpisah (ini bukan properti ".length" seperti dalam beberapa bahasa lain). Ini bukan cacat, per-se. Setiap pengembang C dan C ++ dapat menulis kode yang benar hanya dengan meneruskan panjang sebagai parameter jika diperlukan. Masalahnya adalah beberapa API yang membutuhkan informasi ini gagal menetapkannya sebagai parameter. Atau diasumsikan bahwa beberapa konstanta MAX_BUFFER_SIZE akan digunakan. API semacam itu sekarang sudah tidak digunakan lagi dan diganti dengan API alternatif yang memungkinkan ukuran array / buffer / string ditentukan.

Scanf (Menjawab Pertanyaan Terakhir Anda)
Secara pribadi, saya menggunakan pustaka C ++ iostreams (std :: cin, std :: cout, operator << dan >>, std :: getline, std :: istringstream, std :: ostringstream , dll.), jadi saya biasanya tidak berurusan dengan itu. Jika saya terpaksa menggunakan C murni, saya pribadi hanya akan menggunakan fgetc () atau getchar () dalam kombinasi dengan strtol () , strtoul () , dll. Dan mengurai hal-hal secara manual, karena saya bukan penggemar berat varargs atau format string. Karena itu, sejauh pengetahuan saya, tidak ada masalah dengan [f] scanf () , [f] printf (), dll. selama Anda membuat string format sendiri, Anda tidak pernah melewatkan string format arbitrer atau mengizinkan input pengguna untuk digunakan sebagai string format, dan Anda menggunakan makro pemformatan yang ditentukan di <inttypes.h> jika sesuai. (Catatan, snprintf () harus digunakan sebagai pengganti sprintf () , tetapi itu berkaitan dengan kegagalan menentukan ukuran buffer tujuan dan bukan penggunaan string format). Saya juga harus menunjukkan bahwa, di C ++, boost :: format menyediakan format seperti printf tanpa varargs.

Michael Aaron Safyan
sumber
4
"Deprecated" adalah kata yang kuat, yang memiliki arti khusus saat membahas Standar C ++. Dalam hal ini, gets (), strcpy () dll tidak digunakan lagi.
4
Asalkan Anda membedakan antara "deprecated by the C standard", "deprecated by the Michael Aaron Safyan", dan "deprecated by person or people unknown yang semoga tahu apa yang mereka bicarakan [rujukan?]". Pertanyaannya adalah tentang gaya pengkodean yang disukai, bukan tentang standar C, jadi dua yang kedua sesuai. Tetapi seperti Neil, saya membutuhkan pengambilan ganda sebelum menyadari bahwa pernyataan Anda tidak bermaksud untuk menyiratkan makna pertama.
Steve Jessop
11
strncpyumumnya juga harus dihindari. Itu tidak melakukan apa yang diasumsikan oleh sebagian besar programmer. Itu tidak menjamin penghentian (menyebabkan buffer overruns), dan itu mengisi string yang lebih pendek (mungkin menurunkan kinerja dalam beberapa kasus).
Adrian McCarthy
2
@ Adrian: Saya setuju dengan Anda - baik strncpy()atau bahkan lebih buruk strncat()adalah pengganti yang masuk akal untuk varian n-less.
Jonathan Leffler
4
Jawaban ini menyebarkan dogma yang tidak masuk akal tentang "ganti strcpy dengan strncpy - saya tidak tahu mengapa tapi Microsoft menyuruh saya". strncpy tidak pernah dimaksudkan sebagai versi aman dari strcpy! Di hadapan itu jauh lebih tidak aman. Lihat Mengapa strlcpy dan strlcat dianggap tidak aman? .
Lundin
24

Sekali lagi orang mengulang, seperti mantra, pernyataan menggelikan bahwa versi "n" dari fungsi str adalah versi yang aman.

Jika itu yang dimaksudkan untuk mereka maka mereka akan selalu membatalkan string.

Versi "n" dari fungsi tersebut ditulis untuk digunakan dengan bidang panjang tetap (seperti entri direktori di sistem file awal) di mana terminator nul hanya diperlukan jika string tidak mengisi bidang. Ini juga alasan mengapa fungsi memiliki efek samping aneh yang tidak efisien jika hanya digunakan sebagai pengganti - ambil strncpy () misalnya:

Jika array yang ditunjuk oleh s2 adalah string yang lebih pendek dari n byte, null byte ditambahkan ke salinan dalam array yang ditunjukkan oleh s1, sampai semua n byte ditulis.

Karena buffer yang dialokasikan untuk menangani nama file biasanya berukuran 4kbytes, hal ini dapat menyebabkan penurunan kinerja yang sangat besar.

Jika Anda menginginkan versi yang "seharusnya" aman, dapatkan - atau tulis sendiri - rutinitas strl Anda (strlcpy, strlcat dll) yang selalu nul mengakhiri string dan tidak memiliki efek samping. Harap dicatat bahwa ini tidak benar-benar aman karena mereka dapat memotong string secara diam-diam - ini jarang merupakan tindakan terbaik dalam program dunia nyata mana pun. Ada kalanya hal ini baik-baik saja tetapi ada juga banyak keadaan di mana hal itu dapat menyebabkan hasil bencana (misalnya mencetak resep medis).

Dipstick
sumber
1
Anda benar tentang strncpy(), tapi salah tentang strncat(). strncat()tidak dirancang untuk digunakan dengan bidang dengan panjang tetap - itu sebenarnya dirancang untuk menjadi strcat()yang membatasi jumlah karakter yang digabungkan. Sangat mudah untuk menggunakan ini sebagai "aman strcat()" dengan melacak ruang yang tersisa di buffer saat melakukan beberapa penggabungan, dan bahkan lebih mudah menggunakannya sebagai "aman strcpy()" (dengan menyetel karakter pertama dari buffer tujuan ke '\0'before menyebutnya). strncat() selalu mengakhiri string tujuan, dan tidak menulis '\0's ekstra .
caf
2
@caf - yes but strncat () sama sekali tidak berguna karena dibutuhkan sebagai parameter panjang maksimum yang akan disalin - bukan panjang buffer tujuan. Untuk mencegah buffer overflow, Anda perlu mengetahui panjang string tujuan saat ini, dan jika Anda tahu mengapa Anda menggunakan strncat () - yang harus menyelesaikan panjang tujuan lagi - bukan hanya strlcat () string sumber ke akhir string tujuan.
Dipstick
Itu masih tidak mengubah fakta bahwa Anda menyiratkan bahwa strncat()tidak selalu membatalkan tujuan, dan itu dirancang untuk digunakan dengan bidang dengan panjang tetap, keduanya salah.
caf
2
@chrisharris: strncat()akan berfungsi dengan baik terlepas dari panjang string sumber, sementara strcat()tidak. Masalahnya di strlcat()sini adalah bahwa ini bukan fungsi C standar.
David Thornley
@caf - Anda benar tentang strncat (). Saya tidak pernah benar-benar menggunakannya karena - seperti yang saya tunjukkan di atas - itu sama sekali tidak berguna. Karena itu, hal itu tetap harus dihindari.
Dipstick
19

Beberapa jawaban di sini menyarankan penggunaan strncat()over strcat(); Saya menyarankan bahwa strncat()(dan strncpy()) juga harus dihindari. Ini memiliki masalah yang membuatnya sulit untuk digunakan dengan benar dan menyebabkan bug:

  • parameter panjang ke strncat()terkait dengan (tetapi tidak persis sama - lihat poin ke-3) jumlah maksimum karakter yang dapat disalin ke tujuan daripada ukuran buffer tujuan. Ini membuat strncat()lebih sulit untuk digunakan daripada yang seharusnya, terutama jika beberapa item akan digabungkan ke tujuan.
  • sulit untuk menentukan apakah hasilnya terpotong (yang mungkin penting atau tidak)
  • mudah sekali mengalami kesalahan off-by-one. Seperti catatan standar C99, "Jadi, jumlah maksimum karakter yang dapat berakhir dalam larik yang ditunjukkan oleh s1adalah strlen(s1)+n+1" untuk panggilan yang terlihat sepertistrncat( s1, s2, n)

strncpy()juga memiliki masalah yang dapat mengakibatkan bug yang Anda coba gunakan dengan cara yang intuitif - hal itu tidak menjamin bahwa tujuan tersebut dibatalkan. Untuk memastikan bahwa Anda harus memastikan Anda secara khusus menangani kasus sudut itu dengan meletakkan '\0'sendiri di lokasi terakhir buffer (setidaknya dalam situasi tertentu).

Saya menyarankan untuk menggunakan sesuatu seperti OpenBSD strlcat()dan strlcpy()(meskipun saya tahu bahwa beberapa orang tidak menyukai fungsi tersebut; Saya yakin mereka jauh lebih mudah digunakan dengan aman daripada strncat()/ strncpy()).

Inilah sedikit dari apa yang Todd Miller dan Theo de Raadt katakan tentang masalah dengan strncat()dan strncpy():

Ada beberapa masalah yang ditemui saat strncpy()dan strncat()digunakan sebagai versi aman dari strcpy()dan strcat(). Kedua fungsi tersebut berhubungan dengan terminasi NUL dan parameter panjang dengan cara yang berbeda dan tidak intuitif yang bahkan membingungkan pemrogram berpengalaman. Mereka juga tidak menyediakan cara mudah untuk mendeteksi saat pemotongan terjadi. ... Dari semua masalah ini, kebingungan yang disebabkan oleh parameter panjang dan masalah terkait penghentian NUL adalah yang paling penting. Saat kami mengaudit struktur pohon sumber OpenBSD untuk potensi lubang keamanan, kami menemukan penyalahgunaan yang merajalelastrncpy() dan strncat(). Meskipun tidak semua ini mengakibatkan lubang keamanan yang dapat dieksploitasi, mereka menjelaskan bahwa aturan penggunaan strncpy()dan strncat()dalam operasi tali aman disalahpahami secara luas.

Audit keamanan OpenBSD menemukan bahwa bug dengan fungsi ini "merajalela". Berbeda dengan gets()fungsi tersebut dapat digunakan dengan aman, tetapi dalam praktiknya ada banyak masalah karena antarmukanya membingungkan, tidak intuitif, dan sulit digunakan dengan benar. Saya tahu bahwa Microsoft juga telah melakukan analisis (meskipun saya tidak tahu berapa banyak data mereka yang mungkin telah mereka terbitkan), dan akibatnya telah melarang (atau setidaknya sangat tidak dianjurkan - 'larangan' mungkin tidak mutlak) penggunaan strncat()dan strncpy()(di antara fungsi lainnya).

Beberapa tautan dengan informasi lebih lanjut:

Michael Burr
sumber
1
Jauh lebih baik untuk melacak panjang senar di luar senar itu sendiri. Seperti itu, menggabungkan dua string (-with-length) dengan aman menjadi masalah kalkulasi sederhana (untuk mendapatkan ukuran buffer yang diperlukan) kemungkinan realokasi, dan a memmove(). (Nah, Anda dapat menggunakan memcpy()dalam kasus normal ketika stringnya independen.)
Donal Fellows
1
Poin kedua Anda salah - strncat() selalu mengakhiri string tujuan.
caf
1
Poin kedua Anda salah strncat(). Namun, itu benar strncpy(), yang memiliki beberapa masalah lain. strncat()adalah pengganti yang wajar untuk strcat(), tetapi strncpy()bukan pengganti yang wajar untuk strcpy().
David Thornley
2
caf dan David 100% benar tentang klaim saya yang strncat()tidak selalu batal. Saya membingungkan perilaku strncat()dan strncpy()sedikit (alasan lain mengapa mereka berfungsi untuk dihindari - mereka memiliki nama yang menyiratkan perilaku serupa, tetapi pada kenyataannya berperilaku berbeda dalam cara yang penting ...). Saya telah mengubah jawaban saya untuk mengoreksi ini serta menambahkan informasi tambahan.
Michael Burr
1
Perhatikan bahwa char str[N] = ""; strncat(str, "long string", sizeof(str));buffer overflow jika N tidak cukup besar. The strncat()Fungsi terlalu mudah untuk penyalahgunaan; itu tidak boleh digunakan. Jika Anda dapat menggunakan strncat()dengan aman, Anda dapat menggunakan memmove()atau memcpy()sebagai gantinya (dan itu akan lebih efisien).
Jonathan Leffler
9

Fungsi pustaka standar yang tidak boleh digunakan:

setjmp.h

  • setjmp(). Bersama-sama longjmp(), fungsi-fungsi ini secara luas dikenal sebagai sangat berbahaya untuk digunakan: mereka mengarah pada pemrograman spaghetti, mereka datang dengan berbagai bentuk perilaku yang tidak ditentukan, mereka dapat menyebabkan efek samping yang tidak diinginkan dalam lingkungan program, seperti mempengaruhi nilai yang disimpan di stack. Referensi: MISRA-C: 2012 rule 21.4, CERT C MSC22-C .
  • longjmp(). Lihat setjmp().

stdio.h

  • gets(). Fungsi tersebut telah dihapus dari bahasa C (sesuai C11), karena tidak aman sesuai desain. Fungsi tersebut sudah ditandai sebagai usang di C99. Gunakan fgets()sebagai gantinya. Referensi: ISO 9899: 2011 K.3.5.4.1, lihat juga catatan 404.

stdlib.h

  • atoi()keluarga fungsi. Ini tidak memiliki penanganan kesalahan tetapi memanggil perilaku tidak ditentukan setiap kali terjadi kesalahan. Fungsi yang benar-benar berlebihan yang dapat diganti dengan fungsi strtol()keluarga. Referensi: MISRA-C: 2012 aturan 21.7.

string.h

  • strncat(). Memiliki antarmuka yang canggung yang sering disalahgunakan. Ini sebagian besar merupakan fungsi yang tidak berguna. Lihat juga komentar untuk strncpy().
  • strncpy(). Tujuan dari fungsi ini tidak pernah menjadi versi yang lebih aman strcpy(). Tujuan utamanya adalah selalu untuk menangani format string kuno pada sistem Unix, dan yang dimasukkan ke dalam pustaka standar adalah kesalahan yang diketahui. Fungsi ini berbahaya karena dapat meninggalkan string tanpa penghentian null dan pemrogram diketahui sering salah menggunakannya. Referensi: Mengapa strlcpy dan strlcat dianggap tidak aman? .

Fungsi pustaka standar yang harus digunakan dengan hati-hati:

assert.h

  • assert(). Dilengkapi dengan overhead dan umumnya tidak boleh digunakan dalam kode produksi. Lebih baik menggunakan penanganan kesalahan khusus aplikasi yang menampilkan kesalahan tetapi tidak selalu menutup seluruh program.

signal.h

stdarg.h

  • va_arg()keluarga fungsi. Kehadiran fungsi variabel-panjang dalam program C hampir selalu merupakan indikasi desain program yang buruk. Harus dihindari kecuali Anda memiliki persyaratan yang sangat spesifik.

stdio.h
Umumnya, seluruh pustaka ini tidak direkomendasikan untuk kode produksi , karena terdapat banyak kasus perilaku yang tidak terdefinisi dengan baik dan keamanan jenis yang buruk.

  • fflush(). Sangat bagus untuk digunakan untuk aliran keluaran. Memanggil perilaku yang tidak ditentukan jika digunakan untuk aliran input.
  • gets_s(). Versi aman gets()termasuk dalam antarmuka pemeriksaan batas C11. Ini lebih disukai untuk digunakanfgets() sebagai gantinya, sesuai rekomendasi standar C. Referensi: ISO 9899: 2011 K.3.5.4.1.
  • printf()keluarga fungsi. Fungsi sumber daya berat yang datang dengan banyak perilaku tidak terdefinisi dan jenis keamanan yang buruk.sprintf()juga memiliki kerentanan. Fungsi ini harus dihindari dalam kode produksi. Referensi: MISRA-C: 2012 aturan 21.6.
  • scanf()keluarga fungsi. Lihat komentar tentang printf(). Juga, - scanf()rentan terhadap buffer overruns jika tidak digunakan dengan benar. fgets()lebih disukai digunakan jika memungkinkan. Referensi: CERT C INT05-C , MISRA-C: 2012 aturan 21.6.
  • tmpfile()keluarga fungsi. Hadir dengan berbagai masalah kerentanan. Referensi: CERT C FIO21-C .

stdlib.h

  • malloc()keluarga fungsi. Sangat bagus untuk digunakan dalam sistem yang dihosting, meskipun waspadalah terhadap masalah umum di C90 dan karena itu jangan berikan hasilnya . The malloc()keluarga fungsi tidak boleh digunakan dalam berdiri bebas aplikasi. Referensi: MISRA-C: 2012 aturan 21.3.

    Juga perhatikan itu realloc()berbahaya jika Anda menimpa penunjuk lama dengan hasil realloc(). Jika fungsinya gagal, Anda membuat kebocoran.

  • system(). Hadir dengan banyak overhead dan meskipun portabel, seringkali lebih baik menggunakan fungsi API khusus sistem sebagai gantinya. Hadir dengan berbagai perilaku yang tidak terdefinisi dengan baik. Referensi: CERT C ENV33-C .

string.h

  • strcat(). Lihat komentar untukstrcpy() .
  • strcpy(). Baik-baik saja untuk digunakan, kecuali ukuran data yang akan disalin tidak diketahui atau lebih besar dari buffer tujuan. Jika tidak dilakukan pemeriksaan ukuran data yang masuk, mungkin ada buffer overruns. Yang bukan karena kesalahan strcpy()dirinya sendiri, tetapi dari aplikasi panggilan - yang strcpy()tidak aman sebagian besar adalah mitos yang dibuat oleh Microsoft .
  • strtok(). Mengubah string pemanggil dan menggunakan variabel status internal, yang dapat membuatnya tidak aman di lingkungan multi-utas.
Lundin
sumber
Saya sarankan merekomendasikan static_assert()di tempat assert(), jika kondisi dapat diselesaikan pada waktu kompilasi. Juga, sprintf()hampir selalu bisa diganti snprintf()yang sedikit lebih aman.
pengguna694733
1
Menggunakan strtok()dalam fungsi A berarti bahwa (a) fungsi tersebut tidak dapat memanggil fungsi lain yang juga digunakan strtok()saat A menggunakannya, dan (b) berarti bahwa tidak ada fungsi yang memanggil A dapat digunakan strtok()saat memanggil A. Dengan kata lain, menggunakan strtok()meracuni rantai panggilan; itu tidak dapat digunakan dengan aman dalam kode perpustakaan karena harus dokumen yang digunakan strtok()untuk mencegah pengguna lain strtok()memanggil kode perpustakaan.
Jonathan Leffler
Ini strncpyadalah fungsi yang tepat untuk digunakan saat menulis buffer string dengan bantalan nol dengan data yang diambil dari string yang diakhiri nol atau buffer dengan bantalan nol yang ukurannya setidaknya sebesar tujuan. Buffer tanpa bantalan tidak terlalu umum, tapi juga tidak terlalu jelas.
supercat
7

Beberapa orang akan mengklaim itu strcpydan strcatharus dihindari, demi strncpydanstrncat . Ini agak subjektif, menurut saya.

Mereka harus dihindari saat berurusan dengan input pengguna - tidak diragukan lagi di sini.

Dalam kode "jauh" dari pengguna, ketika Anda hanya tahu buffernya cukup panjang, strcpydan strcatmungkin sedikit lebih efisien karena menghitung nto pass ke sepupu mereka mungkin berlebihan.

Eli Bendersky
sumber
4
Dan jika tersedia, lebih baik lagi strlcatdan strlcpy, karena versi 'n' tidak menjamin penghentian NULL dari string tujuan.
Dan Andreatta
Kedengarannya seperti pengoptimalan yang terlalu dini bagi saya. Tetapi IIRC, strncpy()akan menulis menulis dengan tepat nbyte, menggunakan karakter nul jika perlu. Sebagai Dan, menggunakan versi aman adalah IMO pilihan terbaik.
Bastien Léonard
@Dan, sayangnya fungsi-fungsi ini tidak standar, dan karenanya tidak termasuk dalam diskusi ini
Eli Bendersky
@Eli: tetapi fakta bahwa strncat()mungkin sulit untuk digunakan dengan benar dan aman (dan harus dihindari) ada di topik.
Michael Burr
@ Michael: Saya tidak akan mengatakan sulit untuk menggunakan dengan benar dan aman. Dengan hati-hati, ini berfungsi dengan baik
Eli Bendersky
6

Menghindari

  • strtok untuk program multithread karena tidak thread-safe.
  • gets karena dapat menyebabkan buffer overflow
codaddict
sumber
2
Mereka adalah kasus yang sedikit berbeda. Anda dapat menggunakan strtok dengan aman jika Anda tahu program Anda tidak multithread atau Anda entah bagaimana mengunci akses ke sana, tetapi Anda tidak dapat menggunakannya dengan aman jadi jangan pernah menggunakannya.
jcoder
9
Masalah dengan strtok()berjalan sedikit di luar keamanan utas - itu tidak aman bahkan dalam program utas tunggal kecuali Anda tahu pasti bahwa tidak ada fungsi yang mungkin dipanggil kode Anda saat menggunakan strtok()tidak juga menggunakannya (atau mereka akan mengacaukan strtok()status benar dari bawah Anda). Faktanya, sebagian besar kompiler yang menargetkan platform multi-threaded menangani strtok()potensi masalah sejauh thread berjalan dengan menggunakan penyimpanan lokal thread untuk strtok()data statis. Tapi itu masih tidak memperbaiki masalah fungsi lain yang menggunakannya saat Anda berada (di utas yang sama).
Michael Burr
Memang dan saya akan diam sekarang karena saya tidak ingin mendorong siapa pun untuk menggunakan strtok karena menderita banyak masalah. Aku hanya ingin menunjukkan bahwa itu berbeda dari mendapat meskipun karena adalah mungkin untuk menggunakannya dengan aman sedangkan tidak mungkin untuk digunakan mendapat aman.
jcoder
@Jimmy: Seringkali ada ekstensi pustaka yang tidak standar, atau Anda dapat membuatnya sendiri. Masalah besarnya strtok()adalah ia menyimpan satu buffer dengan tepat untuk bekerja, jadi sebagian besar penggantian yang baik memerlukan menjaga nilai buffer di sekitar dan meneruskannya.
David Thornley
strcspnmelakukan sebagian besar dari yang Anda butuhkan - temukan pemisah token berikutnya. Anda dapat menerapkan kembali varian yang waras strtokdengannya.
bluss
5

Mungkin perlu ditambahkan lagi yang strncpy()bukan pengganti tujuan umum untuk strcpy()yang mungkin disarankan oleh namanya. Ini dirancang untuk bidang dengan panjang tetap yang tidak memerlukan nul-terminator (ini pada awalnya dirancang untuk digunakan dengan entri direktori UNIX, tetapi dapat berguna untuk hal-hal seperti bidang kunci enkripsi).

Namun, mudah digunakan strncat()sebagai pengganti untuk strcpy():

if (dest_size > 0)
{
    dest[0] = '\0';
    strncat(dest, source, dest_size - 1);
}

( ifTes jelas dapat dibatalkan dalam kasus umum, di mana Anda tahu itu dest_sizepasti bukan nol).

kafe
sumber
5

Lihat juga daftar API terlarang Microsoft . Ini adalah API (termasuk banyak yang sudah terdaftar di sini) yang dilarang dari kode Microsoft karena sering disalahgunakan dan menyebabkan masalah keamanan.

Anda mungkin tidak setuju dengan semuanya, tetapi semuanya layak dipertimbangkan. Mereka menambahkan API ke daftar ketika penyalahgunaannya menyebabkan sejumlah bug keamanan.

Adrian McCarthy
sumber
2

Hampir semua fungsi yang berhubungan dengan string yang dihentikan NUL berpotensi tidak aman. Jika Anda menerima data dari dunia luar dan memanipulasinya melalui fungsi str * () maka Anda menyiapkan diri Anda untuk bencana

rep_movsd
sumber
2

Jangan lupa tentang sprintf - ini adalah penyebab dari banyak masalah. Ini benar karena alternatifnya, snprintf terkadang memiliki implementasi berbeda yang dapat membuat kode Anda tidak dapat dibawa.

  1. linux: http://linux.die.net/man/3/snprintf

  2. windows: http://msdn.microsoft.com/en-us/library/2ts7cx93%28VS.71%29.aspx

Dalam kasus 1 (linux) nilai yang dikembalikan adalah jumlah data yang dibutuhkan untuk menyimpan seluruh buffer (jika lebih kecil dari ukuran buffer yang diberikan maka keluarannya dipotong)

Dalam kasus 2 (windows) nilai yang dikembalikan adalah angka negatif jika output terpotong.

Umumnya, Anda harus menghindari fungsi yang bukan:

  1. buffer overflow safe (banyak fungsi telah disebutkan di sini)

  2. benang aman / tidak reentrant (strtok misalnya)

Dalam manual setiap fungsi Anda harus mencari kata kunci seperti: aman, sinkron, asinkron, utas, buffer, bug

INS
sumber
2
_sprintf()dibuat oleh Microsoft sebelum standar snprintf()tiba, IIRC. ´StringCbPrintf () ´ sangat mirip snprintf().
Bastien Léonard
Anda dapat menggunakan sprintfentah bagaimana dengan aman dalam beberapa kasus: sprintf(buffer,"%10s",input);membatasi nb disalin byte ke 10 (jika bufferini char buffer[11]itu aman bahkan jika data mungkin angin terpotong.
Jean-François Fabre
2

Sangat sulit untuk digunakan scanfdengan aman. Penggunaan yang baik dari scanfdapat menghindari buffer overflows, tetapi Anda masih rentan terhadap perilaku tidak terdefinisi saat membaca angka yang tidak sesuai dengan jenis yang diminta. Dalam kebanyakan kasus, fgetsdiikuti oleh self-parsing (menggunakan sscanf, strchr, dll) adalah pilihan yang lebih baik.

Tapi saya tidak akan mengatakan "hindari scanfsepanjang waktu". scanfmemiliki kegunaannya. Sebagai contoh, katakanlah Anda ingin membaca input pengguna dalam chararray yang panjangnya 10 byte. Anda ingin menghapus baris baru, jika ada. Jika pengguna memasukkan lebih dari 9 karakter sebelum baris baru, Anda ingin menyimpan 9 karakter pertama dalam buffer dan membuang semuanya hingga baris baru berikutnya. Anda dapat melakukan:

char buf[10];
scanf("%9[^\n]%*[^\n]", buf));
getchar();

Setelah Anda terbiasa dengan idiom ini, itu lebih pendek dan dalam beberapa hal lebih bersih daripada:

char buf[10];
if (fgets(buf, sizeof buf, stdin) != NULL) {
    char *nl;
    if ((nl = strrchr(buf, '\n')) == NULL) {
        int c;
        while ((c = getchar()) != EOF && c != '\n') {
            ;
        }
    } else {
        *nl = 0;
    }
}
Alok Singhal
sumber
0

Dalam semua skenario string-copy / move - strcat (), strncat (), strcpy (), strncpy (), dll. - semuanya berjalan lebih baik ( lebih aman ) jika beberapa heuristik sederhana diterapkan:

   1. Selalu NUL-fill buffer Anda sebelum menambahkan data.
   2. Deklarasikan buffer karakter sebagai [SIZE + 1], dengan konstanta makro.

Misalnya, diberikan:

#define   BUFSIZE   10
char      Buffer[BUFSIZE+1] = { 0x00 };  /* The compiler NUL-fills the rest */

kita bisa menggunakan kode seperti:

memset(Buffer,0x00,sizeof(Buffer));
strncpy(Buffer,BUFSIZE,"12345678901234567890");

relatif aman. Memset () harus muncul sebelum strncpy (), meskipun kita menginisialisasi Buffer pada waktu kompilasi, karena kita tidak tahu sampah apa yang ditempatkan kode lain sebelum fungsi kita dipanggil. Strncpy () akan memotong data yang disalin menjadi "1234567890", dan tidak akan menghentikannya NUL. Namun, karena kita sudah mengisi NUL seluruh buffer - sizeof (Buffer), bukan BUFSIZE - tetap ada jaminan akan ada akhir "out-of-scope" yang menghentikan NUL, selama kita membatasi penulisan kita menggunakan BUFSIZE konstan, bukan sizeof (Buffer).

Buffer dan BUFSIZE juga berfungsi dengan baik untuk snprintf ():

memset(Buffer,0x00,sizeof(Buffer));
if(snprintf(Buffer,BUFIZE,"Data: %s","Too much data") > BUFSIZE) {
    /* Do some error-handling */
}   /* If using MFC, you need if(... < 0), instead */

Meskipun snprintf () secara khusus hanya menulis BUFIZE-1 karakter, diikuti oleh NUL, ini bekerja dengan aman. Jadi kita "membuang" byte NUL yang asing di akhir Buffer ... kita mencegah kondisi buffer-overflow dan string yang tidak ditentukan, untuk biaya memori yang cukup kecil.

Panggilan saya di strcat () dan strncat () lebih tegas: jangan gunakan mereka. Sulit untuk menggunakan strcat () dengan aman, dan API untuk strncat () sangat berlawanan dengan intuisi sehingga upaya yang diperlukan untuk menggunakannya dengan benar meniadakan manfaat apa pun. Saya mengusulkan drop-in berikut:

#define strncat(target,source,bufsize) snprintf(target,source,"%s%s",target,source)

Sangat menggoda untuk membuat drop-in strcat (), tetapi bukan ide yang bagus:

#define strcat(target,source) snprintf(target,sizeof(target),"%s%s",target,source)

karena target mungkin sebuah pointer (sehingga sizeof () tidak mengembalikan informasi yang kita butuhkan). Saya tidak memiliki solusi "universal" yang baik untuk instance strcat () di kode Anda.

Masalah yang sering saya temui dari programmer "strFunc () - aware" adalah upaya untuk melindungi dari buffer-overflows dengan menggunakan strlen (). Ini bagus jika konten dijamin akan dihentikan NUL. Jika tidak, strlen () itu sendiri dapat menyebabkan kesalahan buffer-overrun (biasanya mengarah ke pelanggaran segmentasi atau situasi core-dump lainnya), sebelum Anda mencapai kode "bermasalah" yang Anda coba lindungi.

TLR
sumber
-2

atoi tidak aman untuk benang. Saya menggunakan strtol sebagai gantinya, sesuai rekomendasi dari halaman manual.

Fred
sumber
5
Sepertinya ini berlaku untuk satu implementasi tertentu. Tidak ada alasan mengapa strtol()menjadi thread-safe dan atoi()tidak akan aman.
David Thornley
2
Rekomendasi untuk menggunakan strtol tidak ada hubungannya dengan keamanan thread, tetapi dengan penanganan error. Tidak yakin dari halaman manual mana Anda mendapatkan info itu - saya tidak dapat menemukan rekomendasi apa pun di bawah ini man atoi(meskipun demikian).
Lundin