Ketika saya mencoba mengkompilasi kode C yang menggunakan gets()
fungsi dengan GCC, saya mendapatkan peringatan ini:
(.text + 0x34): peringatan: fungsi `mendapat 'berbahaya dan tidak boleh digunakan.
Saya ingat ini ada hubungannya dengan perlindungan dan keamanan tumpukan, tapi saya tidak yakin persis mengapa.
Bagaimana saya bisa menghapus peringatan ini dan mengapa ada peringatan seperti itu tentang penggunaan gets()
?
Jika gets()
sangat berbahaya lalu mengapa kita tidak bisa menghapusnya?
c
fgets
buffer-overflow
gets
vinit dhatrak
sumber
sumber
gets()
Buffer_overflow_attackgets()
Jawaban:
Untuk menggunakan
gets
dengan aman, Anda harus tahu persis berapa banyak karakter yang akan Anda baca, sehingga Anda dapat membuat buffer Anda cukup besar. Anda hanya akan tahu bahwa jika Anda tahu persis data apa yang akan Anda baca.Alih-alih menggunakan
gets
, Anda ingin menggunakanfgets
, yang memiliki tanda tangan(
fgets
, jika membaca seluruh baris, akan meninggalkan'\n'
string; Anda harus menghadapinya.)Itu tetap merupakan bagian resmi dari bahasa hingga standar ISO C 1999, tetapi secara resmi dihapus oleh standar 2011. Sebagian besar implementasi C masih mendukungnya, tetapi setidaknya gcc mengeluarkan peringatan untuk kode apa pun yang menggunakannya.
sumber
gets()
yang menyebabkan kompiler memancarkan peringatan saat digunakan.Kenapa
gets()
berbahaya?Worm internet pertama ( Morris Internet Worm ) melarikan diri sekitar 30 tahun yang lalu (1988-11-02), dan menggunakan
gets()
buffer overflow sebagai salah satu metode penyebaran dari sistem ke sistem. Masalah dasarnya adalah bahwa fungsi tersebut tidak tahu seberapa besar buffer, sehingga terus membaca sampai menemukan baris baru atau bertemu EOF, dan mungkin meluap batas buffer yang diberikan.Anda harus lupa bahwa Anda pernah mendengar yang
gets()
ada.Standar C11 ISO / IEC 9899: 2011 dihilangkan
gets()
sebagai fungsi standar, yaitu A Good Thing ™ (secara resmi ditandai sebagai 'usang' dan 'tidak digunakan lagi' dalam ISO / IEC 9899: 1999 / Cor.3: 2007 - Technical Corrigendum 3 untuk C99, dan kemudian dihapus di C11). Sayangnya, itu akan tetap di perpustakaan selama bertahun-tahun (yang berarti 'dekade') karena alasan kompatibilitas. Jika terserah saya, implementasigets()
akan menjadi:Mengingat kode Anda akan macet, cepat atau lambat, lebih baik untuk mengatasi masalah lebih cepat daripada nanti. Saya siap menambahkan pesan kesalahan:
Versi modern dari sistem kompilasi Linux menghasilkan peringatan jika Anda menautkan
gets()
- dan juga untuk beberapa fungsi lain yang juga memiliki masalah keamanan (mktemp()
, ...).Alternatif untuk
gets()
fgets ()
Seperti orang lain berkata, alternatif kanonik untuk
gets()
yangfgets()
menentukanstdin
sebagai file streaming.Apa yang belum disebutkan oleh orang lain adalah yang
gets()
tidak termasuk baris baru tetapifgets()
tidak. Jadi, Anda mungkin perlu menggunakan pembungkusfgets()
yang menghapus baris baru:Atau lebih baik:
Juga, seperti yang ditunjukkan oleh caf dalam komentar dan paxdiablo menunjukkan dalam jawabannya, dengan
fgets()
Anda mungkin memiliki data yang tersisa pada satu baris. Kode pembungkus saya membiarkan data itu untuk dibaca lain kali; Anda dapat dengan mudah memodifikasinya untuk melahap sisa data jika Anda lebih suka:Masalah residual adalah bagaimana melaporkan tiga status hasil yang berbeda - EOF atau kesalahan, pembacaan baris dan tidak terpotong, dan sebagian pembacaan baris tetapi data terpotong.
Masalah ini tidak muncul dengan
gets()
karena tidak tahu di mana buffer Anda berakhir dan riang menginjak-injak ujungnya, mendatangkan malapetaka pada tata letak memori cenderung indah Anda, sering mengacaukan tumpukan kembali ( Stack Overflow ) jika buffer dialokasikan pada tumpukan, atau menginjak-injak informasi kontrol jika buffer dialokasikan secara dinamis, atau menyalin data ke variabel global (atau modul) berharga lainnya jika buffer dialokasikan secara statis. Tak satu pun dari ini adalah ide yang baik - mereka melambangkan ungkapan 'perilaku tidak terdefinisi`.Ada juga TR 24731-1 (Laporan Teknis dari Komite Standar C) yang memberikan alternatif yang lebih aman untuk berbagai fungsi, termasuk
gets()
:Kompiler Microsoft Visual Studio menerapkan perkiraan pada standar TR 24731-1, tetapi ada perbedaan antara tanda tangan yang diterapkan oleh Microsoft dan yang ada di TR.
Standar C11, ISO / IEC 9899-2011, termasuk TR24731 dalam Lampiran K sebagai bagian opsional dari perpustakaan. Sayangnya, ini jarang diimplementasikan pada sistem mirip Unix.
getline()
- POSIXPOSIX 2008 juga menyediakan alternatif yang aman untuk
gets()
dipanggilgetline()
. Ini mengalokasikan ruang untuk garis secara dinamis, sehingga Anda akhirnya harus membebaskannya. Karena itu menghilangkan batasan pada panjang garis. Ini juga mengembalikan panjang data yang telah dibaca, atau-1
(dan tidakEOF
!), Yang berarti bahwa byte nol dalam input dapat ditangani dengan andal. Ada juga variasi 'pilih pembatas karakter tunggal Anda' yang disebutgetdelim()
; ini dapat berguna jika Anda berurusan dengan output darifind -print0
mana ujung nama file ditandai dengan'\0'
karakter ASCII NUL , misalnya.sumber
fgets()
danfgets_wrapper()
versi Anda akan meninggalkan bagian tambahan dari garis yang terlalu panjang di buffer input, untuk dibaca oleh fungsi input selanjutnya. Dalam banyak kasus, Anda ingin membaca dan membuang karakter-karakter ini.getline()
dan relatifnyagetdelim()
, yang mengembalikan panjang 'baris' yang dibaca oleh perintah, mengalokasikan ruang yang diperlukan untuk dapat menyimpan seluruh baris. Bahkan itu dapat menyebabkan masalah jika Anda berakhir dengan file JSON satu baris yang beberapa gigabytes; dapatkah Anda mendapatkan semua ingatan itu? (Dan sementara kita berada di sana, dapatkah kita memilikistrcpy()
danstrcat()
varian yang mengembalikan pointer ke byte nol di akhir? Dll)fgets()
adalah jika file tersebut berisi byte nol, Anda tidak dapat menentukan berapa banyak data yang ada setelah byte nol hingga akhir baris (atau EOF).strlen()
hanya dapat melaporkan hingga byte nol dalam data; setelah itu, ini adalah dugaan dan karena itu hampir pasti salah.gets()
ada." Ketika saya melakukan ini, saya bertemu dengannya lagi dan kembali ke sini. Apakah Anda meretas stackoverflow untuk mendapatkan upvotes?Karena
gets
tidak melakukan pemeriksaan apa pun saat mendapatkan byte dari stdin dan meletakkannya di suatu tempat. Contoh sederhana:Sekarang, pertama-tama Anda diperbolehkan memasukkan berapa banyak karakter yang Anda inginkan,
gets
tidak akan mempedulikannya. Kedua byte di atas ukuran array di mana Anda meletakkannya (dalam hal iniarray1
) akan menimpa apa pun yang mereka temukan di memori karenagets
akan menuliskannya. Pada contoh sebelumnya, ini berarti bahwa jika Anda memasukkan"abcdefghijklmnopqrts"
mungkin, tidak dapat diprediksi, itu akan menimpa jugaarray2
atau apa pun.Fungsi ini tidak aman karena mengasumsikan input yang konsisten. TIDAK PERNAH MENGGUNAKANNYA!
sumber
gets
outright tidak dapat digunakan adalah tidak memiliki parameter panjang / jumlah array yang diperlukan; seandainya itu ada, itu hanya akan menjadi fungsi standar C biasa.gets
, dan mengapa tidak ada varian fiting standar yang dibuat nyaman untuk kasus penggunaan di mana baris baru tidak diinginkan sebagai bagian dari input?gets
, seperti namanya, dirancang untuk mendapatkan string daristdin
, namun alasan untuk tidak memiliki parameter ukuran mungkin berasal dari semangat C : Trust the programmer. Fungsi ini telah dihapus di C11 dan penggantian yang diberikangets_s
mengambil ukuran buffer input. Saya tidak tahu tentangfgets
bagian itu.gets
mungkin dapat dimaafkan adalah jika seseorang menggunakan sistem I / O buffer-line perangkat keras yang secara fisik tidak mampu mengirimkan garis pada panjang tertentu, dan masa hidup program yang dimaksudkan. lebih pendek dari masa pakai perangkat keras. Dalam hal ini, jika perangkat keras tidak mampu mengirimkan garis lebih dari 127 byte, mungkin dibenarkan untukgets
menjadi buffer 128-byte, meskipun saya akan berpikir keuntungan dari dapat menentukan buffer yang lebih pendek ketika mengharapkan input yang lebih kecil akan lebih dari membenarkan biaya.gets
danstrcat
menerima dengan aman sebanyak yang sesuai.Anda tidak boleh menggunakannya
gets
karena tidak ada cara untuk menghentikan buffer overflow. Jika pengguna mengetik lebih banyak data daripada yang dapat ditampung di buffer Anda, kemungkinan besar Anda akan berakhir dengan korupsi atau lebih buruk.Bahkan, ISO telah benar-benar mengambil langkah menghapus
gets
dari standar C (pada C11, meskipun sudah tidak digunakan dalam C99) yang, mengingat seberapa tinggi mereka menilai kompatibilitas ke belakang, harus menjadi indikasi seberapa buruk fungsi itu.Hal yang benar untuk dilakukan adalah menggunakan
fgets
fungsi denganstdin
pegangan file karena Anda dapat membatasi karakter yang dibaca dari pengguna.Tetapi ini juga memiliki masalah seperti:
Untuk itu, hampir setiap kode C di beberapa titik dalam karir mereka akan menulis pembungkus yang lebih berguna
fgets
juga. Ini milik saya:dengan beberapa kode uji:
Ini memberikan perlindungan yang sama seperti
fgets
yang mencegah buffer overflows tetapi juga memberi tahu pemanggil tentang apa yang terjadi dan membersihkan karakter berlebih sehingga tidak mempengaruhi operasi input Anda berikutnya.Jangan ragu untuk menggunakannya sesuai keinginan, saya dengan ini merilisnya di bawah lisensi "melakukan apa yang Anda inginkan" :-)
sumber
gets()
baik di bagian 7.19.7.7 di mana ia didefinisikan atau di bagian 7.26.9 Arah perpustakaan masa depan dan sub-bagian untuk<stdio.h>
. Bahkan tidak ada catatan kaki di situ yang berbahaya. (Karena itu, saya melihat "Sudah usang dalam ISO / IEC 9899: 1999 / Cor.3: 2007 (E))" dalam jawaban oleh Yu Hao .) Tapi C11 memang menghapusnya dari standar - dan tidak sebelum waktu!int getLine (char *prmpt, char *buff, size_t sz) { ... if (fgets (buff, sz, stdin) == NULL)
menyembunyikansize_t
untukint
konversisz
.sz > INT_MAX || sz < 2
akan menangkap nilai anehsz
.if (buff[strlen(buff)-1] != '\n') {
adalah eksploitasi peretas karena karakter pertama pengguna jahat yang dimasukkan bisa berupa karakter nol render yang melekat padabuff[strlen(buff)-1]
UB.while (((ch = getchar())...
memiliki masalah jika pengguna memasukkan karakter nol.uang .
Untuk membaca dari stdin:
sumber
Anda tidak dapat menghapus fungsi API tanpa merusak API. Jika Anda mau, banyak aplikasi tidak lagi dapat dikompilasi atau dijalankan.
Inilah alasan yang diberikan oleh satu referensi :
sumber
Baru-baru ini saya membaca, di pos USENET
comp.lang.c
, yanggets()
dihapus dari Standar. WOO HOOsumber
gcc -std=c2012 -pedantic ...
mendapat () tidak akan berhasil. (Saya baru saja membuat-std
parameter)Di C11 (ISO / IEC 9899: 201x),
gets()
telah dihapus. (Sudah usang dalam ISO / IEC 9899: 1999 / Cor.3: 2007 (E))Selain itu
fgets()
, C11 memperkenalkan alternatif aman barugets_s()
:Namun, di bagian Praktik yang disarankan ,
fgets()
masih lebih disukai.sumber
gets()
berbahaya karena mungkin bagi pengguna untuk crash program dengan mengetik terlalu banyak ke prompt. Itu tidak dapat mendeteksi akhir dari memori yang tersedia, jadi jika Anda mengalokasikan sejumlah memori terlalu kecil untuk tujuan itu, itu dapat menyebabkan kesalahan seg dan crash. Kadang-kadang tampaknya sangat tidak mungkin bahwa pengguna akan mengetik 1000 huruf menjadi prompt yang dimaksudkan untuk nama seseorang, tetapi sebagai programmer, kita perlu membuat program kita antipeluru. (Ini juga bisa menjadi risiko keamanan jika pengguna dapat merusak program sistem dengan mengirimkan terlalu banyak data).fgets()
memungkinkan Anda menentukan berapa banyak karakter yang dikeluarkan dari buffer input standar, sehingga mereka tidak membanjiri variabel.sumber
Saya ingin menyampaikan undangan yang sungguh-sungguh kepada semua pengelola perpustakaan C di luar sana yang masih termasuk
gets
di perpustakaan mereka "kalau-kalau ada yang masih bergantung padanya": Silakan ganti implementasi Anda dengan yang setara denganIni akan membantu memastikan tidak ada yang masih bergantung padanya. Terima kasih.
sumber
Fungsi C mendapat berbahaya dan merupakan kesalahan yang sangat mahal. Tony Hoare memilihnya untuk disebutkan secara spesifik dalam ceramahnya "Null Referensi: The Billion Dollar Kesalahan":
http://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare
Seluruh jam layak ditonton tetapi untuk tampilan komentarnya dari 30 menit dengan spesifik mendapat kritik sekitar 39 menit.
Semoga ini membangkitkan selera Anda untuk seluruh pembicaraan, yang menarik perhatian pada bagaimana kita membutuhkan lebih banyak bukti kebenaran formal dalam bahasa dan bagaimana perancang bahasa harus disalahkan atas kesalahan dalam bahasa mereka, bukan pada programmer. Ini tampaknya telah menjadi alasan yang meragukan bagi perancang bahasa yang buruk untuk menyalahkan para programmer dengan kedok 'kebebasan programmer'.
sumber