Seberapa berbahaya mengakses array di luar batasnya (dalam C)? Kadang-kadang dapat terjadi bahwa saya membaca dari luar array (saya sekarang mengerti saya kemudian mengakses memori yang digunakan oleh beberapa bagian lain dari program saya atau bahkan lebih dari itu) atau saya mencoba untuk menetapkan nilai ke indeks di luar array. Program terkadang macet, tetapi terkadang hanya berjalan, hanya memberikan hasil yang tidak terduga.
Sekarang yang ingin saya ketahui adalah, seberapa berbahayakah ini sebenarnya? Jika itu merusak program saya, itu tidak terlalu buruk. Jika di sisi lain itu merusak sesuatu di luar program saya, karena saya entah bagaimana berhasil mengakses beberapa memori yang sama sekali tidak terkait, maka itu sangat buruk, saya kira. Saya membaca banyak 'apa pun bisa terjadi', 'segmentasi mungkin menjadi masalah paling tidak buruk' , 'hard disk Anda mungkin menjadi merah muda dan unicorn mungkin bernyanyi di bawah jendela Anda', yang semuanya bagus, tapi apa sebenarnya bahayanya?
Pertanyaan saya:
- Bisakah membaca nilai dari jauh di luar array merusak apa pun selain program saya? Saya akan membayangkan hanya melihat hal-hal tidak mengubah apa pun, atau apakah itu misalnya mengubah atribut 'terakhir kali dibuka' dari file yang kebetulan saya jangkau?
- Dapatkah menetapkan nilai-nilai di luar array merusak sesuatu selain dari program saya? Dari pertanyaan Stack Overflow ini saya menyimpulkan bahwa dimungkinkan untuk mengakses lokasi memori mana pun, bahwa tidak ada jaminan keamanan.
- Saya sekarang menjalankan program kecil saya dari dalam XCode. Apakah itu memberikan perlindungan ekstra di sekitar program saya di mana ia tidak dapat menjangkau di luar ingatannya sendiri? Bisakah itu membahayakan XCode?
- Adakah rekomendasi tentang cara menjalankan kode bawaan kereta saya dengan aman?
Saya menggunakan OSX 10.7, Xcode 4.6.
Jawaban:
Sejauh standar ISO C (definisi resmi bahasa) yang bersangkutan, mengakses array di luar batasnya memiliki " perilaku tidak terdefinisi ". Arti harfiah dari ini adalah:
Catatan non-normatif memperluas ini:
Jadi itu teorinya. Apa kenyataannya?
Dalam kasus "terbaik", Anda akan mengakses sebagian memori yang dimiliki oleh program yang sedang berjalan (yang mungkin menyebabkan program Anda salah tingkah), atau yang tidak dimiliki oleh program yang sedang berjalan (yang mungkin akan menyebabkan program Anda crash dengan sesuatu seperti kesalahan segmentasi). Atau Anda mungkin mencoba menulis ke memori yang dimiliki program Anda, tetapi itu ditandai hanya baca; ini mungkin juga akan menyebabkan program Anda macet.
Itu dengan asumsi program Anda berjalan di bawah sistem operasi yang mencoba untuk melindungi proses yang berjalan bersamaan dari satu sama lain. Jika kode Anda berjalan pada "bare metal", katakanlah jika itu bagian dari kernel OS atau sistem tertanam, maka tidak ada perlindungan seperti itu; kode perilaku buruk Anda adalah apa yang seharusnya memberikan perlindungan itu. Dalam hal itu, kemungkinan kerusakan jauh lebih besar, termasuk, dalam beberapa kasus, kerusakan fisik pada perangkat keras (atau benda-benda atau orang-orang di sekitarnya).
Bahkan dalam lingkungan OS yang dilindungi, perlindungan tidak selalu 100%. Ada bug sistem operasi yang mengizinkan program yang tidak terjangkau untuk mendapatkan akses root (administratif), misalnya. Bahkan dengan hak pengguna biasa, program yang tidak berfungsi dapat menggunakan sumber daya yang berlebihan (CPU, memori, disk), mungkin menjatuhkan seluruh sistem. Banyak malware (virus, dll.) Mengeksploitasi buffer overruns untuk mendapatkan akses tidak sah ke sistem.
(Satu contoh historis: Saya pernah mendengar bahwa pada beberapa sistem lama dengan memori inti , berulang kali mengakses satu lokasi memori dalam satu lingkaran ketat dapat benar-benar menyebabkan potongan memori meleleh. Kemungkinan lain termasuk menghancurkan layar CRT, dan memindahkan bacaan. / tulis kepala drive disk dengan frekuensi harmonik kabinet drive, yang menyebabkannya melintasi meja dan jatuh ke lantai.)
Dan selalu ada Skynet yang perlu dikhawatirkan.
Intinya adalah ini: jika Anda dapat menulis sebuah program untuk melakukan sesuatu yang buruk dengan sengaja , setidaknya secara teoritis mungkin bahwa program kereta bisa melakukan hal yang sama secara tidak sengaja .
Dalam praktiknya, sangat tidak mungkin program kereta Anda berjalan pada sistem MacOS X akan melakukan sesuatu yang lebih serius daripada crash. Tapi itu tidak mungkin untuk sepenuhnya mencegah kode buggy dari melakukan hal-hal yang sangat buruk.
sumber
Secara umum, Sistem Operasi saat ini (yang paling populer) menjalankan semua aplikasi di wilayah memori yang dilindungi menggunakan manajer memori virtual. Ternyata tidak MUDAH (per katakan) cukup membaca atau menulis ke lokasi yang ada di ruang NYATA di luar wilayah yang telah ditugaskan / dialokasikan untuk proses Anda.
Jawaban langsung:
1) Membaca hampir tidak akan pernah secara langsung merusak proses lain, namun secara tidak langsung dapat merusak proses jika Anda membaca nilai KUNCI yang digunakan untuk mengenkripsi, mendekripsi, atau memvalidasi program / proses. Pembacaan di luar batas dapat berdampak buruk / tidak terduga pada kode Anda jika Anda membuat keputusan berdasarkan data yang Anda baca
2) Satu-satunya cara Anda dapat benar-benar KERUSAKAN sesuatu dengan menulis ke tempat yang dapat diakses oleh alamat memori adalah jika alamat memori yang Anda tulis sebenarnya adalah register perangkat keras (lokasi yang sebenarnya bukan untuk penyimpanan data tetapi untuk mengendalikan beberapa bagian perangkat keras) bukan lokasi RAM. Pada kenyataannya, Anda biasanya tidak akan merusak sesuatu kecuali Anda menulis suatu lokasi terprogram yang tidak dapat ditulis ulang (atau sesuatu yang sifatnya seperti itu).
3) Secara umum berjalan dari dalam debugger menjalankan kode dalam mode debug. Berjalan dalam mode debug cenderung untuk (tetapi tidak selalu) menghentikan kode Anda lebih cepat ketika Anda telah melakukan sesuatu yang dianggap tidak praktis atau benar-benar ilegal.
4) Jangan pernah menggunakan makro, gunakan struktur data yang sudah memiliki batas indeks array memeriksa built in, dll ....
TAMBAHAN Saya harus menambahkan bahwa informasi di atas hanya untuk sistem yang menggunakan sistem operasi dengan jendela perlindungan memori. Jika menulis kode untuk sistem tertanam atau bahkan sistem yang memanfaatkan sistem operasi (real-time atau lainnya) yang tidak memiliki jendela perlindungan memori (atau jendela yang ditangani secara virtual), orang harus lebih berhati-hati dalam membaca dan menulis ke memori. Juga dalam kasus ini praktik pengkodean AMAN dan AMAN harus selalu digunakan untuk menghindari masalah keamanan.
sumber
Tidak memeriksa batas dapat menyebabkan efek samping yang buruk, termasuk lubang keamanan. Salah satu yang jelek adalah eksekusi kode arbitrer . Dalam contoh klasik: jika Anda memiliki array ukuran tetap, dan gunakan
strcpy()
untuk meletakkan string yang disediakan pengguna di sana, pengguna dapat memberi Anda string yang meluap buffer dan menimpa lokasi memori lainnya, termasuk alamat kode tempat CPU harus kembali ketika fungsi Anda selesai.Yang berarti pengguna Anda dapat mengirim Anda string yang akan membuat program Anda pada dasarnya menelepon
exec("/bin/sh")
, yang akan mengubahnya menjadi shell, mengeksekusi apa pun yang dia inginkan di sistem Anda, termasuk memanen semua data Anda dan mengubah mesin Anda menjadi botnet node.Lihat Menghancurkan Stack Untuk Kesenangan dan Keuntungan untuk detail tentang bagaimana hal ini dapat dilakukan.
sumber
foo[0]
melaluifoo[len-1]
setelah sebelumnya digunakan ceklen
terhadap panjang array baik mengeksekusi atau melewatkan sepotong kode, compiler harus merasa bebas untuk menjalankan kode lainnya tanpa syarat bahkan jika aplikasi memiliki penyimpanan melewati array dan efek dari membacanya akan jinak, tetapi efek dari memohon kode lain tidak akan.Anda menulis:
Mari kita begini: memuat pistol. Arahkan di luar jendela tanpa tujuan dan api tertentu. Apa bahayanya?
Masalahnya adalah Anda tidak tahu. Jika kode Anda menimpa sesuatu yang membuat crash program Anda, Anda akan baik-baik saja karena akan menghentikannya ke status yang ditentukan. Namun jika tidak macet maka masalah mulai muncul. Sumber daya apa yang berada di bawah kendali program Anda dan apa pengaruhnya terhadap mereka? Sumber daya apa yang mungkin berada di bawah kendali program Anda dan apa pengaruhnya terhadap mereka? Saya tahu setidaknya satu masalah besar yang disebabkan oleh luapan seperti itu. Masalahnya adalah dalam fungsi statistik yang tampaknya tidak berarti yang mengacaukan beberapa tabel konversi yang tidak terkait untuk database produksi. Hasilnya adalah beberapa pembersihan yang sangat mahal sesudahnya. Sebenarnya itu akan jauh lebih murah dan lebih mudah untuk ditangani jika masalah ini akan memformat hard disk ... dengan kata lain: unicorn merah muda mungkin menjadi masalah Anda.
Gagasan bahwa sistem operasi Anda akan melindungi Anda optimis. Jika mungkin cobalah untuk menghindari menulis di luar batas.
sumber
Tidak menjalankan program Anda sebagai root atau pengguna istimewa lainnya tidak akan membahayakan sistem Anda, jadi umumnya ini adalah ide yang bagus.
Dengan menulis data ke beberapa lokasi memori acak Anda tidak akan secara langsung "merusak" program lain yang berjalan di komputer Anda karena setiap proses berjalan di ruang memori itu sendiri.
Jika Anda mencoba mengakses memori apa pun yang tidak dialokasikan untuk proses Anda, sistem operasi akan menghentikan program Anda dari mengeksekusi dengan kesalahan segmentasi.
Jadi secara langsung (tanpa menjalankan sebagai root dan langsung mengakses file seperti / dev / mem) tidak ada bahaya bahwa program Anda akan mengganggu program lain yang berjalan pada sistem operasi Anda.
Namun demikian - dan mungkin inilah yang Anda dengar dalam hal bahaya - dengan secara acak menulis data acak ke lokasi memori acak, Anda yakin dapat merusak apa pun yang dapat Anda rusak.
Misalnya program Anda mungkin ingin menghapus file tertentu yang diberikan oleh nama file yang disimpan di suatu tempat di program Anda. Jika secara tidak sengaja Anda hanya menimpa lokasi tempat nama file disimpan, Anda mungkin akan menghapus file yang sangat berbeda.
sumber
NSArray
s di Objective-C diberi blok memori tertentu. Melebihi batas array berarti Anda akan mengakses memori yang tidak ditetapkan ke array. Ini berarti:Dari aspek program Anda, Anda selalu ingin tahu kapan kode Anda melebihi batas array. Ini dapat menyebabkan nilai yang tidak diketahui dikembalikan, menyebabkan aplikasi Anda mogok atau memberikan data yang tidak valid.
sumber
NSArrays
memiliki pengecualian di luar batas. Dan pertanyaan ini sepertinya tentang C array.Anda mungkin ingin mencoba menggunakan
memcheck
alat di Valgrind ketika Anda menguji kode Anda - itu tidak akan menangkap pelanggaran batas array individu dalam bingkai tumpukan, tetapi harus menangkap berbagai jenis masalah memori lainnya, termasuk yang akan menyebabkan masalah lebih halus, lebih luas masalah di luar ruang lingkup fungsi tunggal.Dari manual:
ETA: Meskipun, seperti jawaban Kaz mengatakan, itu bukan obat mujarab, dan tidak selalu memberikan hasil yang paling membantu, terutama ketika Anda menggunakan pola akses yang menarik .
sumber
Jika Anda pernah melakukan pemrograman tingkat sistem atau pemrograman sistem tertanam, hal-hal yang sangat buruk dapat terjadi jika Anda menulis ke lokasi memori acak. Sistem yang lebih lama dan banyak pengontrol mikro menggunakan memori yang dipetakan IO, jadi menulis ke lokasi memori yang memetakan ke register periferal dapat mendatangkan malapetaka, terutama jika itu dilakukan secara tidak sinkron.
Contohnya adalah pemrograman memori flash. Mode pemrograman pada chip memori diaktifkan dengan menulis urutan nilai tertentu ke lokasi tertentu di dalam kisaran alamat chip. Jika proses lain adalah menulis ke lokasi lain dalam chip saat itu sedang berlangsung, itu akan menyebabkan siklus pemrograman gagal.
Dalam beberapa kasus, perangkat keras akan membungkus alamat (sebagian besar bit / byte alamat diabaikan) sehingga penulisan ke alamat di luar akhir ruang alamat fisik sebenarnya akan menghasilkan data yang ditulis tepat di tengah-tengah hal.
Dan akhirnya, CPU yang lebih lama seperti MC68000 dapat dikunci hingga hanya reset perangkat keras yang dapat membuatnya berjalan kembali. Belum bekerja pada mereka selama beberapa dekade tapi saya percaya itu ketika mengalami kesalahan bus (memori tidak ada) ketika mencoba menangani pengecualian, itu hanya akan berhenti sampai reset perangkat keras ditegaskan.
Rekomendasi terbesar saya adalah steker terang-terangan untuk suatu produk, tetapi saya tidak memiliki kepentingan pribadi di dalamnya dan saya tidak berafiliasi dengan mereka dengan cara apa pun - tetapi berdasarkan beberapa dekade pemrograman C dan sistem tertanam di mana keandalan sangat penting, PC Gimpel Lint tidak hanya akan mendeteksi kesalahan semacam itu, itu akan membuat programmer C / C ++ yang lebih baik keluar dari Anda dengan terus-menerus mengomeli Anda tentang kebiasaan buruk.
Saya juga merekomendasikan membaca standar pengkodean MISRA C, jika Anda dapat mengambil salinan dari seseorang. Saya belum melihat yang baru-baru ini tetapi di masa lalu mereka memberikan penjelasan yang baik tentang mengapa Anda harus / tidak harus melakukan hal-hal yang mereka liput.
Entah tentang Anda, tetapi sekitar ke-2 atau ke-3 saya mendapatkan coredump atau hangup dari aplikasi apa pun, pendapat saya tentang perusahaan apa pun yang menghasilkannya turun setengahnya. Kali ke-4 atau ke-5 dan apa pun paketnya menjadi rak buku dan saya menggerakkan pasak kayu melalui bagian tengah paket / cakram itu masuk hanya untuk memastikan tidak pernah kembali menghantui saya.
sumber
Saya bekerja dengan kompiler untuk chip DSP yang sengaja menghasilkan kode yang mengakses satu melewati akhir array keluar dari kode C yang tidak!
Ini karena loop disusun sehingga akhir iterasi mengambil beberapa data untuk iterasi berikutnya. Jadi datum yang diambil pada akhir iterasi terakhir tidak pernah benar-benar digunakan.
Menulis kode C seperti itu memanggil perilaku yang tidak terdefinisi, tetapi itu hanya formalitas dari dokumen standar yang berkaitan dengan portabilitas maksimal.
Lebih sering tidak, program yang mengakses di luar batas tidak dioptimalkan secara cerdik. Itu hanya buggy. Kode mengambil beberapa nilai sampah dan, tidak seperti loop dioptimalkan dari kompiler yang disebutkan di atas, kode kemudian menggunakan nilai dalam perhitungan berikutnya, sehingga merusak mereka.
Perlu menangkap bug seperti itu, dan karenanya layak membuat perilaku tidak terdefinisi hanya untuk alasan itu saja: sehingga run-time dapat menghasilkan pesan diagnostik seperti "array overrun pada baris 42 dari main.c".
Pada sistem dengan memori virtual, sebuah array dapat dialokasikan sedemikian rupa sehingga alamat yang mengikutinya berada di area memori virtual yang tidak dipetakan. Akses kemudian akan mengebom program.
Namun demikian, akses ke nilai yang tidak diinisialisasi atau keluar dari batas kadang-kadang merupakan teknik optimasi yang valid, bahkan jika tidak portabel secara maksimal. Ini adalah contohnya mengapa alat Valgrind tidak melaporkan akses ke data yang tidak diinisialisasi ketika akses tersebut terjadi, tetapi hanya ketika nilainya kemudian digunakan dalam beberapa cara yang dapat mempengaruhi hasil program. Anda mendapatkan diagnostik seperti "cabang bersyarat dalam xxx: nnn tergantung pada nilai yang tidak diinisialisasi" dan terkadang sulit untuk melacak dari mana asalnya. Jika semua akses semacam itu langsung terjebak, akan ada banyak kesalahan positif yang timbul dari kode yang dioptimalkan kompiler serta kode yang dioptimalkan dengan tangan dengan benar.
Omong-omong, saya bekerja dengan beberapa codec dari vendor yang mengeluarkan kesalahan ini ketika porting ke Linux dan berjalan di bawah Valgrind. Tetapi vendor meyakinkan saya bahwa hanya beberapa bitnilai yang digunakan sebenarnya berasal dari memori yang tidak diinisialisasi, dan bit-bit itu dengan hati-hati dihindari oleh logika .. Hanya bit yang baik dari nilai yang digunakan dan Valgrind tidak memiliki kemampuan untuk melacak ke bit individual. Materi yang tidak diinisialisasi berasal dari membaca sepatah kata melewati akhir bit stream data yang dikodekan, tetapi kode tahu berapa banyak bit dalam aliran dan tidak akan menggunakan lebih banyak bit daripada yang sebenarnya. Karena akses di luar akhir bit stream array tidak menyebabkan kerusakan pada arsitektur DSP (tidak ada memori virtual setelah array, tidak ada port yang dipetakan memori, dan alamat tidak membungkus) itu adalah teknik optimasi yang valid.
"Perilaku tidak terdefinisi" tidak terlalu berarti, karena menurut ISO C, cukup menyertakan header yang tidak didefinisikan dalam standar C, atau memanggil fungsi yang tidak didefinisikan dalam program itu sendiri atau standar C, adalah contoh dari tidak terdefinisi tingkah laku. Perilaku tidak terdefinisi tidak berarti "tidak didefinisikan oleh siapa pun di planet ini" hanya "tidak didefinisikan oleh standar ISO C". Namun tentu saja, terkadang perilaku tidak terdefinisi benar - benar tidak didefinisikan oleh siapa pun.
sumber
Selain program Anda sendiri, saya rasa Anda tidak akan merusak apa pun, dalam kasus terburuk Anda akan mencoba membaca atau menulis dari alamat memori yang sesuai dengan halaman yang tidak ditentukan oleh kernel untuk proses Anda, menghasilkan pengecualian yang tepat dan terbunuh (maksud saya, proses Anda).
sumber