Back to Chromium

Using `unsafe` code in first-party Chromium code

docs/rust/unsafe.md

150.0.7861.14.2 KB
Original Source

Using unsafe code in first-party Chromium code

Rust is well-known as a memory-safe language, but it has an escape hatch in the form of unsafe Rust, which permits developers to take responsibility from the compiler. Unsafe Rust can be more powerful or faster than safe Rust, but requires careful and precise design, as well as additional mental effort spent manually ensuring that no undefined behavior is possible.

This document discusses when and how to use unsafe code in first-party Chromium. There is a different policy for unsafe code in third-party libraries.

When to use unsafe

Usage of unsafe Rust is strongly discouraged in Chromium. It opens the door to undefined behavior, which can lead to bugs, crashes, or security vulnerabilities. Whenever possible, you should write your code using only safe Rust.

However, there are times when unsafe code is unavoidable, such as interacting with C/C++ code, or when the performance overhead of safety (from bounds checks, ref-counting, etc) is deemed unacceptable. Developers are permitted to opt in to unsafe Rust by adding the allow_unsafe = true flag to their GN target.

Ultimately, even Rust with unsafe blocks is safer than C++. We trust Chromium developers to use their judgement when choosing when to use unsafe code. Importantly, Using unsafe Rust does not require any additional review or approvals. Unsafe code should be reviewed by local owners just like safe Rust code, albeit with the same extra scrutiny they would apply to other high-risk parts of a CL.

How to use unsafe

Safety comments

Chromium requires that all unsafe code is accompanied by a safety comment. There are two types of safety documentation:

  • Unsafe declarations (unsafe fn, unsafe trait) declare something which is unsafe to use, e.g. a function that dereferences a pointer could cause UB if the pointer is null. The safety comment on a declaration must describe the requirements for the function to be used safely:
Rust
/// Frobinate the glorb
///
/// # Safety
/// * `glorb` must be valid to dereference (non-null, valid, aligned, etc.)
unsafe fn frobinate(glorb: *const Glorb) { ... }

Of course, taking a &Glorb in the first place would be better.

  • Unsafe usages (unsafe {}, unsafe impl, etc) invoke an unsafe item, and must have a safety comment explaining how the requirements in the declaration are met:
Rust
let glorb : Glorb = Glorb::new();
/// SAFETY: the pointer is derived from a reference and is guaranteed to be
/// safe to dereference.
unsafe { frobinate(ptr::from_ref(&glorb)) };

Safety comments don't need to be verbose or rehash basic Rust concepts: you should assume the reader is familiar with the borrow checker, can look up the specific requirements to dereference a pointer, and so on. The most important thing is that the comments are complete and precise: they are the main (and sometimes only) way for readers to reason about safety, so they must have all the relevant information.

In the future the presence of safety comments will be enforced at compile time via a clippy lint.

Usage Guidelines

  • Write a safe implementation first. You can always retrofit it with unsafe if you really need the performance later.

  • Keep it minimal as much as possible:

    • Encapsulate the unsafety: avoid exposing unsafe APIs, and rely only on local invariants that you can enforce yourself.

    • unsafe blocks should wrap only the parts that are actually unsafe:

      Rust
      // Bad
      unsafe { do_something_dangerous(do_something_safe()) };
      
      // Good
      let x = do_something_safe();
      unsafe { do_something_dangerous(x) };
      
  • Be precise: Document all safety conditions in your comments. If something isn't documented, don't rely on it for safety.

  • Be pedantic: Safety considerations can be subtle; it's a good idea to familiarize yourself with unsafe resources like the Rustonomicon, and do web searches if you're even a little unclear about anything.