programowanieInżynier systemowy / Starszy programista Rust

Czym jest Unsafe Rust, do czego jest potrzebny, jakie są jego zasady i jak poprawnie używać bloków unsafe, aby minimalizować ryzyko?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź

Unsafe Rust to rozszerzenie bezpiecznego podzbioru Rust, które pozwala na wykonywanie operacji, których kompilator nie jest w stanie sprawdzić pod kątem poprawności własności, cykli życia i aliasowania. Główne obszary zastosowań: interakcja z niskopoziomowymi bibliotekami, FFI, ręczna kontrola pamięci, realizacja abstrahencji, które nie mieszczą się w modelu bezpiecznego Rust.

Kluczowe cechy unsafe:

  • Wymagana jest wyraźna deklaracja bloku: unsafe { ... } lub funkcji: unsafe fn some_func()
  • W bloku unsafe dozwolone są niebezpieczne operacje: dereferencja raw pointer, wywołanie niebezpiecznych funkcji i metod, dostęp do union, statycznych zmiennych mutowalnych, realizacja metod niestrukturalnej pamięci
  • Użycie unsafe nie czyni całego kodu w bloku niebezpiecznym dla całego języka — tylko zobowiązuje do ręcznego zagwarantowania poprawności

Przykład:

let x: i32 = 10; let ptr: *const i32 = &x as *const i32; unsafe { println!("Wartość: {}", *ptr); // dereferencja raw pointer }

Pytanie z pułapką

Czy kod wewnątrz bloku unsafe jest całkowicie niebezpieczny i nie sprawdzany przez kompilator, czy kompilator nadal stosuje zasady borrow checkera i inne kontrole?

Odpowiedź: Nie, wewnątrz bloku unsafe kompilator nadal sprawdza wiele zasad Rust (na przykład, typizacja, zasady własności, składnia), ale pozwala na wykonywanie tylko tych działań, które w przeciwnym razie byłyby zabronione. Nie można całkowicie wyłączyć borrow checkera!

Przykład:

let mut x = 0; let r1 = &mut x as *mut i32; // Niedozwolone: let r2 = &mut x as *mut i32; // nawet wewnątrz unsafe pojawią się błędy, jeśli naruszona jest mutowalność

Przykłady rzeczywistych błędów z powodu nieznajomości subtelności tematu


Historia

W asynchronicznej bibliotece plików użyto raw pointer do kontroli bufora, ale zapomniano poprawnie śledzić cykl życia bufora. W rezultacie, podczas zamykania pliku, wskaźnik stał się "wiszący" i dostęp do niego prowadził do zachowania undefined (sekcja unsafe nie uratowała przed use-after-free).


Historia

W własnej implementacji struktury podobnej do Vec programista ręcznie rozszerzał bufor za pomocą unsafe. Błąd przesunięcia doprowadził do nieprawidłowego kopiowania elementów i uszkodzenia danych, ponieważ nie uwzględniono wyrównania i układu typów.


Historia

W deskryptorze wątków użyto statycznego mutowalnego wskaźnika (static mut) bez odpowiedniej synchronizacji. Z tego powodu w trybie wielowątkowym przypadkowo wystąpił wyścig danych, prowadzący do sporadycznego awarii aplikacji, wychwytywanego tylko przez fuzzing.