Skip to content

Unsafe Rust

"Unsafe không phải là đối lập của safe. Nó là nền tảng của safe."

1. Sức mạnh của Unsafe

Những gì Unsafe mở khóa

rust
// Unsafe cho phép 5 hành động bị cấm trong safe Rust:

unsafe {
    // 1. Dereference raw pointer
    let ptr: *const i32 = &42;
    let value = *ptr;
    
    // 2. Gọi unsafe function
    dangerous_function();
    
    // 3. Truy cập mutable static
    GLOBAL_COUNTER += 1;
    
    // 4. Implement unsafe trait
    // impl Sync for MyType {}
    
    // 5. Truy cập union fields
    // let x = my_union.field;
}

Ranh giới Unsafe vs Safe

                    Ranh giới Safety
┌─────────────────────────────────────────────────────┐
│                                                     │
│   Safe Rust (Compiler xác minh)                     │
│   ═════════════════════════════                     │
│   • References luôn hợp lệ                          │
│   • Không có data races                             │
│   • Không có dangling pointers                      │
│   • Không có buffer overflows                       │
│                                                     │
│   ┌─────────────────────────────────────────────┐   │
│   │  Unsafe Block (BẠN xác minh)                │   │
│   │  ════════════════════════                   │   │
│   │  • Raw pointer dereference                  │   │
│   │  • Gọi FFI                                  │   │
│   │  • Custom invariants                        │   │
│   └─────────────────────────────────────────────┘   │
│                                                     │
│   Safe code có thể GỌI unsafe code                  │
│   Nhưng phải ĐÓNG GÓI nó an toàn                    │
│                                                     │
└─────────────────────────────────────────────────────┘

2. Raw Pointers

Các loại Pointer

rust
// Raw pointer bất biến
let x = 42;
let ptr: *const i32 = &x;

// Raw pointer có thể thay đổi
let mut y = 42;
let ptr_mut: *mut i32 = &mut y;

// Khác biệt chính so với references:
// • Có thể null
// • Có thể dangling
// • Có thể alias (nhiều mutable pointers đến cùng data)
// • Không có lifetime tracking
// • Không tự động deref

Tạo Raw Pointers (Safe)

rust
let x = 42;

// Tạo raw pointers là SAFE
let ptr1: *const i32 = &x;         // Từ reference
let ptr2: *const i32 = &x as *const i32;  // Cast tường minh

let ptr3 = Box::into_raw(Box::new(42));  // Từ Box

// Từ địa chỉ tùy ý (nguy hiểm!)
let ptr4 = 0x12345 as *const i32;

Dereference Raw Pointers (Unsafe)

rust
let x = 42;
let ptr: *const i32 = &x;

// UNSAFE: Dereference yêu cầu unsafe block
unsafe {
    println!("Giá trị: {}", *ptr);
}

// Tại sao unsafe? Compiler không thể xác minh:
// • Pointer không null
// • Pointer trỏ đến memory hợp lệ
// • Memory đã được khởi tạo
// • Memory không đang bị mutate ở nơi khác

Null Pointers

rust
use std::ptr;

// Tạo null pointer
let null_ptr: *const i32 = ptr::null();
let null_mut: *mut i32 = ptr::null_mut();

// Kiểm tra null
if !null_ptr.is_null() {
    unsafe { println!("{}", *null_ptr); }
}

// Rust không có "null reference" - dùng Optional<&T> thay thế
let maybe_ref: Option<&i32> = None;  // Thay thế an toàn

Pointer Arithmetic

rust
let arr = [1, 2, 3, 4, 5];
let ptr: *const i32 = arr.as_ptr();

unsafe {
    // Offset theo N phần tử (không phải bytes!)
    let second = *ptr.offset(1);      // arr[1]
    let third = *ptr.add(2);          // arr[2] - cú pháp gọn hơn
    
    // Offset âm
    let ptr_end = ptr.add(4);
    let fourth = *ptr_end.sub(1);     // arr[3]
    
    // Biến thể wrapping (không UB khi overflow)
    let wrapped = ptr.wrapping_add(100);
}

3. FFI (Foreign Function Interface)

Gọi C từ Rust

rust
// Khai báo hàm C external
extern "C" {
    fn abs(input: i32) -> i32;
    fn strlen(s: *const c_char) -> usize;
}

fn main() {
    unsafe {
        let result = abs(-5);
        println!("abs(-5) = {}", result);  // 5
    }
}

Các kiểu FFI

rust
use std::ffi::{c_char, c_int, c_void, CStr, CString};

// Ánh xạ kiểu C
// Kiểu C      → Kiểu Rust
// int         → c_int (i32 trên hầu hết platforms)
// char*       → *const c_char / *mut c_char
// void*       → *const c_void / *mut c_void
// size_t      → usize

Làm việc với C Strings

rust
use std::ffi::{CStr, CString};

// Rust String → C string (để truyền CHO C)
let rust_string = "Xin chào";
let c_string = CString::new(rust_string).unwrap();
let ptr: *const c_char = c_string.as_ptr();

unsafe {
    some_c_function(ptr);
}

// C string → Rust String (từ hàm C)
unsafe {
    let c_ptr: *const c_char = get_string_from_c();
    
    // Borrowed view (zero-copy)
    let c_str = CStr::from_ptr(c_ptr);
    
    // Convert sang Rust String (copy data)
    let rust_string = c_str.to_str().unwrap().to_owned();
}

Liên kết thư viện C

rust
// build.rs
fn main() {
    // Liên kết thư viện hệ thống
    println!("cargo:rustc-link-lib=ssl");
    println!("cargo:rustc-link-lib=crypto");
    
    // Hoặc thư viện static
    println!("cargo:rustc-link-lib=static=mylib");
    println!("cargo:rustc-link-search=native=/path/to/lib");
}

// lib.rs
#[link(name = "mylib")]
extern "C" {
    fn my_c_function(x: c_int) -> c_int;
}

Expose Rust cho C

rust
// Hàm có thể gọi từ C
#[no_mangle]  // Không mangle tên symbol
pub extern "C" fn rust_function(x: i32) -> i32 {
    x * 2
}

// Struct với layout tương thích C
#[repr(C)]
pub struct Point {
    x: f64,
    y: f64,
}

// Tạo header (dùng cbindgen crate)
// Tạo ra: int32_t rust_function(int32_t x);

Checklist Safety FFI

rust
// ⚠️ Mọi FFI call đều unsafe vì:
// 1. Rust không thể xác minh memory safety của C
// 2. C có thể không tuân theo aliasing rules của Rust
// 3. C có thể lưu pointers quá lifetime
// 4. C có thể có vấn đề thread-safety

// Pattern đóng gói an toàn:
pub fn safe_abs(x: i32) -> i32 {
    unsafe { abs(x) }  // Chúng ta biết abs() là pure function
}

// Unsafe nếu không thể đảm bảo safety:
pub unsafe fn dangerous_c_call(ptr: *mut Data) {
    // Caller phải đảm bảo ptr hợp lệ
    c_function_that_stores_ptr(ptr);
}

4. std::mem::transmute

Transmute là gì?

rust
// transmute diễn giải lại bits thành kiểu khác
// CỰC KỲ nguy hiểm - bỏ qua TẤT CẢ type checking

let x: u32 = 0x41424344;
let bytes: [u8; 4] = unsafe { std::mem::transmute(x) };
// bytes = [0x44, 0x43, 0x42, 0x41] trên little-endian

Biểu đồ Transmute

              transmute<A, B>(a)
┌─────────────────────────────────────────────────┐
│                                                 │
│   Kiểu A: u32              Kiểu B: [u8; 4]      │
│   ────────                 ────────────         │
│                                                 │
│   0x41424344      ───▶     [0x44,0x43,0x42,0x41]│
│                                                 │
│   Layout bộ nhớ:                                │
│   ┌────┬────┬────┬────┐    ┌────┬────┬────┬────┐│
│   │ 44 │ 43 │ 42 │ 41 │ == │ 44 │ 43 │ 42 │ 41 ││
│   └────┴────┴────┴────┘    └────┴────┴────┴────┘│
│   (little-endian)          [0]  [1]  [2]  [3]  │
│                                                 │
│   Cùng bits, diễn giải khác                     │
│                                                 │
└─────────────────────────────────────────────────┘

Các thay thế an toàn

rust
// ❌ Dùng transmute
let x: f32 = unsafe { std::mem::transmute(0x3f800000u32) };

// ✅ Dùng from_bits (an toàn)
let x: f32 = f32::from_bits(0x3f800000);

// ❌ Transmute cho pointer cast
let ptr: *const u8 = unsafe { std::mem::transmute(some_ptr) };

// ✅ Dùng as cast
let ptr: *const u8 = some_ptr as *const u8;

// ❌ Transmute references
let r: &u32 = unsafe { std::mem::transmute(&x) };

// ✅ Pointer cast rồi dereference (nếu layout tương thích)
let r: &u32 = unsafe { &*((&x) as *const _ as *const u32) };

Use Cases hợp lệ của Transmute

rust
// 1. Enum sang repr integer (nếu repr(u8/i32/etc))
#[repr(u8)]
enum Color { R = 0, G = 1, B = 2 }
let c: u8 = unsafe { std::mem::transmute(Color::G) };  // 1

// 2. Function pointer casts (cùng signature)
type FnA = fn(i32) -> i32;
type FnB = fn(i32) -> i32;
let f: FnA = |x| x + 1;
let g: FnB = unsafe { std::mem::transmute(f) };

// 3. Extended lifetime (RẤT nguy hiểm)
// Hầu như không bao giờ đúng!

Safety Contract của Transmute

rust
// transmute yêu cầu:
// ✓ Cùng kích thước: size_of::<A>() == size_of::<B>()
// ✓ Bit patterns hợp lệ: B phải chấp nhận mọi bit patterns của A
// ✓ Alignment: alignment của B phải ≤ alignment của A

// Transmutes không hợp lệ (UB):
unsafe {
    // Kích thước khác - không biên dịch được
    // let x: u64 = std::mem::transmute(0u32);
    
    // Bit pattern không hợp lệ
    let b: bool = std::mem::transmute(2u8);  // UB! bool phải là 0 hoặc 1
    
    // Reference từ integer
    let r: &i32 = std::mem::transmute(0usize);  // UB! null reference
}

5. Interior Mutability trong Unsafe Context

UnsafeCell

rust
use std::cell::UnsafeCell;

// UnsafeCell là cách DUY NHẤT để mutate qua &T
pub struct MyCell<T> {
    value: UnsafeCell<T>,
}

impl<T> MyCell<T> {
    pub fn new(value: T) -> Self {
        Self { value: UnsafeCell::new(value) }
    }
    
    pub fn get(&self) -> *mut T {
        self.value.get()  // Trả về raw pointer
    }
    
    pub fn set(&self, value: T) {
        unsafe {
            *self.value.get() = value;
        }
    }
}

🎯 Best Practices

Giảm thiểu Unsafe

rust
// ✅ Đóng gói unsafe trong safe API
pub struct SafeBuffer {
    data: Vec<u8>,
}

impl SafeBuffer {
    pub fn get(&self, index: usize) -> Option<u8> {
        // Kiểm tra bounds an toàn
        self.data.get(index).copied()
    }
    
    pub unsafe fn get_unchecked(&self, index: usize) -> u8 {
        // Tài liệu hóa yêu cầu safety
        *self.data.get_unchecked(index)
    }
}

Tài liệu hóa yêu cầu Safety

rust
/// Dereferences raw pointer thành T.
/// 
/// # Safety
/// 
/// - `ptr` phải không null
/// - `ptr` phải trỏ đến T hợp lệ, đã khởi tạo
/// - T không được mutate đồng thời
/// - T phải sống trong 'a
pub unsafe fn deref_ptr<'a, T>(ptr: *const T) -> &'a T {
    &*ptr
}

Các Undefined Behaviors thường gặp

Hành viVí dụHậu quả
Null deref*ptr::null()Segfault / UB
Use after freeDeref freed pointerHỏng data
Data raceTruy cập mut đồng thờiHỏng data
Giá trị không hợp lệtransmute(2u8) cho boolUB
Truy cập lệch alignmentUnaligned pointer derefCrash