FlawOpen

flawopen.com/Keamanan memori/Rust unsafe

Apakah blok unsafe Rust tetap aman bagi memori?

CWE-787: Penulisan di luar batasHalaman referensi
Jawaban singkat

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

Langkah 1

API aman menerima iterator

Pemanggil memberikan iterator biasa kepada fungsi tanpa prasyarat unsafe.

Langkah 2

Implementasi mencadangkan kapasitas dari petunjuk

Fungsi hanya mencadangkan batas bawah yang dilaporkan, yang bisa lebih kecil dari jumlah item sebenarnya.

Langkah 3

Penulisan tanpa pemeriksaan melewati kapasitas

Implementasi menulis semua item melalui raw pointer tanpa memperbesar alokasi atau memeriksa kapasitas tiap item.

Langkah 4

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.

TIDAK SOUND
// 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);
    }
}
PERBAIKAN AMAN
// 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

Pertanyaan umum

Bisakah unsafe dilarang sepenuhnya?

#![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.

Bisakah kode unsafe memercayai sebuah trait?

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.

Apakah ini melemahkan alasan memilih Rust?

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

Lihat juga Apakah strncpy() lebih aman daripada strcpy()? CWE vs CVE: apa bedanya? Mass Assignment: risiko dan allowlist field yang aman Kebocoran state serverless: isolasi data per permintaan PHP == dan perbandingan Magic Hash