Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Language generalities

Language guarantees

Undefined Behaviors (UB)

The behavior of a program is undefined when its semantics is not described in the Rust language.

The existence of UB is considered an error.

For example, dereferencing the null pointer is a UB. On the other hand, unwraping the None object is well defined because it is the language that processes this error (by launching a panic).

The current list of UBs is given in the language reference. Notice the following guarantees:

  • No dereference of pointer to an unallocated or unaligned memory address (dangling pointer), which implies
    • No buffer overflow
    • No access to freed memory
    • No non-aligned access
  • The pointed values are consistent with the pointer's type. For example, a value pointed at by a boolean pointer will be byte of value 1 or 0.
  • Respect of aliasing rules (see also nomicon): a mutable reference cannot be shared.
  • No concurrent access (reading/writing is not possible while writing) to the same memory address (data race, see also nomicon)

Rust guarantees

The language paradigm is to ensure the absence of a UB in a program using only the non-unsafe part of Rust.

However, the language does not prevent

  • resource leaks (memory, IO, ...),
  • numeric overflows.

References

  • https://doc.rust-lang.org/reference/unsafety.html
  • https://doc.rust-lang.org/nomicon/what-unsafe-does.html

Naming

As of now, the standard library is the de facto standard for naming things in the Rust world. However, an effort has been made to formalize it, first in RFC 430, then in the Rust API Guidelines.

The basic rule consists in using :

  • UpperCamelCase for types, traits, enum variants,
  • snake_case for functions, methods, macros, variables and modules,
  • SCREAMING_SNAKE_CASE for statics and constants,
  • 'lowercase for lifetimes.

The Rust API Guidelines also prescribes more precise naming conventions for some particular constructions:

  • (C-CONV) for conversion methods (as_, to_, into_),
  • (C-GETTER) for getters,
  • (C-ITER) for iterator-producing methods,
  • (C-ITER-TY) for iterator types,
  • (C-FEATURE) for feature naming,
  • (C-WORD-ORDER) for word order consistency.

Rule LANG-NAMING

Development of a secure application must follow the naming conventions outlined in the Rust API Guidelines.

Integer overflows

Although some verification is performed by Rust regarding potential integer overflows, precautions should be taken when executing arithmetic operations on integers.

In particular, it should be noted that using debug or release compilation profile changes integer overflow behavior. In debug configuration, overflow cause the termination of the program (panic), whereas in the release configuration the computed value silently wraps around the maximum value that can be stored.

This last behavior can be made explicit by using the Wrapping generic type, or the overflowing_<op> and wrapping_<op> operations on integers (the <op> part being add, mul, sub, shr, etc.).

use std::num::Wrapping;
use std::panic;

fn main() {
let x: u8 = 242;

let result = panic::catch_unwind(|| {
println!("{}", x + 50);                      // panics in debug, prints 36 in release.
});
if result.is_err() { println!("panic"); }
println!("{}", x.overflowing_add(50).0);     // always prints 36.
println!("{}", x.wrapping_add(50));          // always prints 36.
println!("{}", Wrapping(x) + Wrapping(50));  // always prints 36.

// always panics:
let (res, c) = x.overflowing_add(50);
let result = panic::catch_unwind(|| {
if c { panic!("custom error"); }
else { println!("{}", res); }
});
if result.is_err() { println!("panic"); }
}

Rule LANG-ARITH

When assuming that an arithmetic operation can produce an overflow, the specialized functions overflowing_<op>, wrapping_<op>, or the Wrapping type must be used.

Error handling

The Result type is the preferred way of handling functions that can fail. A Result object must be tested, and never ignored.

Recommendation LANG-ERRWRAP

A crate can implement its own Error type, wrapping all possible errors. It must be careful to make this type exception-safe (RFC 1236), and implement Error + Send + Sync + 'static as well as Display.

Recommendation LANG-ERRDO

The ? operator should be used to improve readability of code. The try! macro should not be used.

Third-party crates may be used to facilitate error handling. Most of them (notably failure, snafu, thiserror) address the creation of new custom error types that implement the necessary traits and allow wrapping other errors.

Another approach (notably proposed in the anyhow crate) consists in an automatic wrapping of errors into a single universal error type. Such wrappers should not be used in libraries and complex systems because they do not allow developers to provide context to the wrapped error.

Panics

Explicit error handling (Result) should always be preferred instead of calling panic. The cause of the error should be available, and generic errors should be avoided.

Crates providing libraries should never use functions or instructions that can fail and cause the code to panic.

Common patterns that can cause panics are:

  • using unwrap or expect,
  • using assert,
  • an unchecked access to an array,
  • integer overflow (in debug mode),
  • division by zero,
  • large allocations,
  • string formatting using format!.

Rule LANG-NOPANIC

Functions or instructions that can cause the code to panic at runtime must not be used.

Rule LANG-ARRINDEXING

Array indexing must be properly tested, or the get method should be used to return an Option.

FFI and panics

When calling Rust code from another language (for ex. C), the Rust code must be careful to never panic. Stack unwinding from Rust code into foreign code results in undefined behavior.

Rule LANG-FFIPANIC

Rust code called from FFI must either ensure the function cannot panic, or use catch_unwind or the std::panic module to ensure the rust code will not abort or return in an unstable state.

Note that catch_unwind will only catch unwinding panics, not those that abort the process.