flawopen.com/Keamanan memori/Rust unsafe
Apakah blok unsafe Rust tetap aman bagi memori?
unsafe tidak mematikan pemeriksaan Rust. Kata ini mengizinkan operasi tertentu yang syarat keamanannya tidak dapat dibuktikan compiler, dan programmer wajib memenuhi syarat tersebut. Akibatnya, abstraksi unsafe yang tidak sound dapat membuat pemanggil biasa dalam kode aman memicu undefined behaviour (UB), meskipun mereka tidak menulis unsafe.
Istilah utama
- Operasi unsafe:
- Operasi dengan syarat keamanan yang tidak dapat dibuktikan compiler, seperti dereference raw pointer.
- Abstraksi sound:
- API aman yang pemanggil amannya tidak dapat gunakan untuk memicu undefined behaviour.
- size_hint():
- Perkiraan ukuran iterator untuk optimasi; bukan hitungan pasti atau bukti keamanan memori.
- Undefined behaviour (UB):
- Perilaku program yang tidak didefinisikan Rust; kode unsafe harus mencegah pemanggil aman memicunya.
Cara abstraksi aman yang tidak sound gagal
API aman menerima iterator
Pemanggil memberikan iterator biasa kepada fungsi tanpa prasyarat unsafe.
Implementasi mencadangkan kapasitas dari petunjuk
Fungsi hanya mencadangkan batas bawah yang dilaporkan, yang bisa lebih kecil dari jumlah item sebenarnya.
Penulisan tanpa pemeriksaan melewati kapasitas
Implementasi menulis semua item melalui raw pointer tanpa memperbesar alokasi atau memeriksa kapasitas tiap item.
Pemakaian aman mencapai UB
Jika item melebihi ruang yang tersedia, pemanggilan aman dapat menulis di luar batas. Gunakan Vec::extend atau implementasi yang menambah kapasitas dengan aman.
// UNSOUND: reserve uses only the lower hint, // then unchecked writes consume every item. fn append_unsound(dst: &mut Vec<u8>, iter: impl Iterator<Item = u8>) { let (lower, _) = iter.size_hint(); dst.reserve(lower); let start = dst.len(); let mut written = 0; unsafe { let out = dst.as_mut_ptr().add(start); for item in iter { // UB once writes exceed spare capacity. out.add(written).write(item); written += 1; } dst.set_len(start + written); } }
// SOUND: Vec::extend handles capacity // growth and length updates safely. fn append(dst: &mut Vec<u8>, iter: impl Iterator<Item = u8>) { dst.extend(iter); }
Makna sebenarnya dari jaminan Rust
Jaminan tepatnya adalah: Rust aman tidak dapat menyebabkan undefined behaviour jika kode unsafe yang diandalkannya sound.Rust Reference mendefinisikan soundness pada batas API: kode unsafe harus memastikan klien aman tidak dapat memicu UB. Compiler tidak memeriksa setiap kewajiban pembuktian di dalam operasi unsafe; programmer dan reviewer harus memenuhinya.
Ini tetap menjadi keunggulan praktis penting dibanding C atau C++: Rust membuat operasi yang sensitif terhadap memori dan kewajiban keamanannya lebih terlihat, sehingga reviewer dapat berfokus pada batas unsafe dan abstraksi aman di atasnya. Batas tersebut mencakup blok unsafe, kontrak fungsi unsafe, implementasi unsafe trait, deklarasi FFI, serta atribut unsafe; cakupannya tidak selalu hanya satu blok kecil. Contoh nyata adalah CVE-2021-25900 pada smallvec: SmallVec::insert_many mencadangkan kapasitas berdasarkan batas bawah size hint iterator lalu menulis item melebihi alokasi; CVSS-nya 9.8.
Standarnya adalah soundness, bukan sekadar kehati-hatian
API disebut tidak sound jika kode aman dapat memakainya untuk memicu undefined behaviour. Contohnya, Iterator::size_hint memberi batas untuk optimasi, bukan jumlah item yang pasti: iterator valid dapat menghasilkan lebih banyak dari batas bawah, sementara iterator yang bermasalah dapat melanggar perkiraannya. Dokumentasi standar secara eksplisit melarang penggunaan hint ini untuk menghilangkan pemeriksaan batas pada kode unsafe. Perilaku trait aman yang keliru juga dapat menimbulkan kesalahan logika atau panic; hal tersebut tidak otomatis menjadi UB, tetapi kode unsafe harus menjaga invariant memori meski kode pemanggil berperilaku tak terduga.
Dampak kode unsafe bersifat nonlokal: invariant memori yang ditetapkan di satu tempat dapat diandalkan oleh API aman di atasnya. Jika invariant itu salah, cacat berada pada abstraksi yang membuka API aman, bukan pada pemanggil aman yang mematuhi kontraknya.
Aturan praktis
- ✓Jangan anggap metode trait aman sebagai bukti keamanan memori.
size_hint()hanya untuk optimasi; pertahankan pemeriksaan atau tambah kapasitas bila perlu. - ✓Pertahankan invariant saat panic.Kode dari pemanggil seperti
Iterator::next, destructor, atau closure dapat melakukan unwinding. Pastikan status yang baru terinisialisasi sebagian tetap valid dan di-drop dengan benar. - ✓Tulis komentar
// SAFETY:yang menyebutkan syarat dan alasan syarat itu terpenuhi. unsafe tanpa penjelasan tidak dapat direview. - ✓Jaga blok tetap sempit. Membungkus seluruh loop hanya untuk mengurangi pengulangan keyword memperluas cakupan yang harus dianalisis reviewer.
- ✓Jalankan Miri pada pengujian yang mengeksekusi jalurnya. Miri dapat mendeteksi sebagian UB pada jalur yang dijalankan, termasuk akses di luar batas, use-after-free, dan aliasing tidak valid; ini bukan bukti soundness.
- ✓Audit dependensi.
cargo auditmemeriksa advisori RustSec yang diketahui;cargo geigermembantu menginventarisasi penggunaan unsafe. Keduanya tidak menggantikan tinjauan kontrak unsafe pada kode yang dikirim.
Pertanyaan umum
#![forbid(unsafe_code)] dapat melarang sintaks unsafe pada crate tempat atribut ini dipasang, berguna untuk crate aplikasi. Aturan ini tidak mencakup dependensi; beberapa implementasi, batas FFI, dan library tetap memerlukan operasi unsafe.
Bisa. unsafe trait mendokumentasikan kewajiban implementasi yang boleh diandalkan kode unsafe. TrustedLen adalah trait pustaka standar yang belum stabil untuk iterator yang menjanjikan size hint tepat. Iterator::size_hint biasa tidak memberi jaminan itu.
Tidak. Model keamanan Rust mempersempit lokasi kewajiban keamanan memori dan membuatnya eksplisit di batas unsafe. Audit kode unsafe, dependensi, kontrak FFI, serta API aman yang dibangun di atas implementasi unsafe tetap diperlukan.
Referensi
- The Rust Reference — unsafe code and soundness
- Rust Reference — the unsafe keyword and proof obligations
- Rust standard library — Iterator::size_hint contract
- Rustonomicon — working with unsafe
- RustSec Advisory RUSTSEC-2021-0003 — smallvec buffer overflow
- GitHub Security Advisory GHSA-43w2-9j62-hq99 — CVE-2021-25900
- The Rustonomicon — the guide to unsafe Rust
- RustSec Advisory Database
- Miri — an interpreter that detects undefined behaviour
- CWE-787 — Out-of-bounds Write
- Case study: the smallvec overflow (CVE-2021-25900)