Rust chặn lỗi bộ nhớ thế nào? Ownership, borrowing và dữ liệu thật từ Android
Tóm tắt
Rust kiểm tra an toàn bộ nhớ lúc biên dịch bằng ownership, borrowing và lifetimes, không cần garbage collector. Với khoảng 5 triệu dòng Rust, Android ghi nhận mật độ lỗ hổng bộ nhớ thấp hơn khoảng 1000 lần, rollback ít hơn 4 lần; phần cần canh là unsafe và lỗi logic.
Nếu 70% lỗ hổng nghiêm trọng đến từ lỗi bộ nhớ, câu hỏi tự nhiên là: có ngôn ngữ nào vừa nhanh như C/C++ vừa không có lớp lỗi đó? Java, Go, C# an toàn bộ nhớ nhờ garbage collector, nhưng đổi lại là runtime và độ trễ khó đoán, không hợp với nhân hệ điều hành hay codec. Rust chọn con đường khác: kiểm tra an toàn bộ nhớ lúc biên dịch, không cần garbage collector. Và giờ đã có dữ liệu thực tế quy mô lớn cho thấy cách này hiệu quả tới mức nào.
Ba quy tắc cốt lõi
| Quy tắc | Nội dung | Chặn lỗi gì |
|---|---|---|
| Ownership | Mỗi giá trị có đúng một chủ; chủ ra khỏi phạm vi thì bộ nhớ được giải phóng tự động | Double free, quên giải phóng |
| Borrowing | Tại một thời điểm: hoặc nhiều tham chiếu chỉ đọc, hoặc đúng một tham chiếu được ghi | Data race, sửa dữ liệu khi đang đọc |
| Lifetimes | Tham chiếu không được sống lâu hơn dữ liệu nó trỏ tới | Use-after-free, con trỏ treo |
let s = String::from("hello");
let r = &s;
drop(s); // lỗi biên dịch: s đang được mượn bởi r
println!("{}", r);
Đoạn tương đương trong C biên dịch được và gây use-after-free; trong Rust, trình biên dịch từ chối ngay.
Những lớp bảo vệ khác
- Kiểm tra biên: truy cập mảng ngoài phạm vi gây panic (dừng có kiểm soát) thay vì đọc/ghi bộ nhớ lạ.
- Không có null: giá trị có thể vắng mặt dùng
Option, bắt buộc xử lý cả hai trường hợp. - Bắt buộc khởi tạo: không thể đọc biến chưa gán giá trị.
- An toàn đa luồng: hệ thống kiểu (
Send,Sync) không cho chia sẻ dữ liệu giữa luồng một cách không an toàn.
Dữ liệu thực tế từ Android
Google công bố số liệu sau khi đưa khoảng 5 triệu dòng Rust vào nền tảng Android:
| Chỉ số | Rust so với C/C++ |
|---|---|
| Mật độ lỗ hổng bộ nhớ | Khoảng 0,2 so với khoảng 1.000 trên mỗi triệu dòng: giảm khoảng 1000 lần |
| Tỷ lệ rollback (thay đổi vừa và lớn) | Thấp hơn khoảng 4 lần |
| Thời gian code review | Ít hơn khoảng 25% |
| Số lần sửa trước khi duyệt | Ít hơn khoảng 20% |
Điểm đáng chú ý: an toàn hơn và nhanh hơn, không phải đánh đổi.
unsafe: lối thoát có kiểm soát
Có việc trình biên dịch không chứng minh được là an toàn (gọi C, thao tác phần cứng, tối ưu đặc biệt). Rust cho đánh dấu khối unsafe. Ở Android, unsafe chiếm khoảng 4% code. Lỗ hổng bộ nhớ đầu tiên trong Rust của Android (CVE-2025-48530, tràn bộ đệm trong CrabbyAVIF) nằm ở loại code này, được vá trước khi phát hành và allocator Scudo đã khiến nó không khai thác được. Google cho rằng unsafe Rust vẫn ít rủi ro hơn C vì khối unsafe không tắt phần lớn kiểm tra, được đóng gói để suy luận cục bộ, và được review kỹ hơn.
Rust không chặn được gì?
- Lỗi logic, phân quyền sai, injection.
- Rò rỉ bộ nhớ (được coi là "an toàn" về mặt ngôn ngữ), deadlock.
- Panic khi truy cập ngoài biên: không bị khai thác nhưng vẫn có thể làm dịch vụ dừng.
- Lỗi trong khối
unsafevà trong thư viện C được gọi qua FFI.
Áp dụng thế nào?
- Dev: Học ownership và borrowing trước (chương 4 của The Rust Book); đây là phần "đánh nhau với trình biên dịch" lúc đầu nhưng là lý do Rust an toàn.
- Tech Lead: Đặt quy tắc review riêng cho mọi khối
unsafe: bắt buộc comment giải thích vì sao an toàn, giữ chúng nhỏ và đóng gói. - PM: Dùng số liệu Android (ít rollback, review nhanh hơn) để thấy Rust không chỉ là chi phí bảo mật mà còn là năng suất.
Nguồn & tham khảo
- Rust in Android: move fast and fix things — Google Security Blog
- What is Ownership? (The Rust Programming Language) — Rust Project