From 686c7a5f5a14f89153879f2403e5515413c0e5b4 Mon Sep 17 00:00:00 2001 From: hg-anssi Date: Tue, 22 Jul 2025 11:07:39 +0200 Subject: [PATCH 01/10] refacto --- src/en/04_language.md | 209 ------------------------ src/en/04_standard.md | 234 ++++++++++++++++++++++++++ src/en/05_central_traits.md | 59 +++++++ src/en/05_errors.md | 86 ++++++++++ src/en/05_guarantees.md | 33 ++++ src/en/05_integer.md | 48 ++++++ src/en/05_macros.md | 8 + src/en/05_naming.md | 30 ++++ src/en/05_typesystem.md | 292 --------------------------------- src/en/SUMMARY.md | 21 ++- src/fr/04_language.md | 225 ------------------------- src/fr/04_standard.md | 252 ++++++++++++++++++++++++++++ src/fr/05_central_traits.md | 66 ++++++++ src/fr/05_errors.md | 101 ++++++++++++ src/fr/05_guarantees.md | 39 +++++ src/fr/05_integer.md | 50 ++++++ src/fr/05_macros.md | 8 + src/fr/05_naming.md | 32 ++++ src/fr/05_typesystem.md | 316 ------------------------------------ src/fr/SUMMARY.md | 21 ++- 20 files changed, 1082 insertions(+), 1048 deletions(-) delete mode 100644 src/en/04_language.md create mode 100644 src/en/04_standard.md create mode 100644 src/en/05_central_traits.md create mode 100644 src/en/05_errors.md create mode 100644 src/en/05_guarantees.md create mode 100644 src/en/05_integer.md create mode 100644 src/en/05_macros.md create mode 100644 src/en/05_naming.md delete mode 100644 src/fr/04_language.md create mode 100644 src/fr/04_standard.md create mode 100644 src/fr/05_central_traits.md create mode 100644 src/fr/05_errors.md create mode 100644 src/fr/05_guarantees.md create mode 100644 src/fr/05_integer.md create mode 100644 src/fr/05_macros.md create mode 100644 src/fr/05_naming.md diff --git a/src/en/04_language.md b/src/en/04_language.md deleted file mode 100644 index f51d715..0000000 --- a/src/en/04_language.md +++ /dev/null @@ -1,209 +0,0 @@ -# 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](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.general). - -For example, dereferencing the null pointer is a *UB*. On the other hand, `unwrap`ing 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](https://doc.rust-lang.org/reference/behavior-considered-undefined.html). 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](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.invalid) 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](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.alias) (see also [nomicon](https://doc.rust-lang.org/nomicon/aliasing.html)): a mutable reference cannot be shared. -* No concurrent access (reading/writing is not possible while writing) to the same memory address ([data race](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.race), see also [nomicon](https://doc.rust-lang.org/nomicon/races.html)) - -### 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 {{#check LANG-NAMING | Respect naming conventions}}** -> -> Development of a secure application must follow the naming conventions -> outlined in the [Rust API Guidelines]. - -[rfc 430]: https://github.com/rust-lang/rfcs/blob/master/text/0430-finalizing-naming-conventions.md -[rust api guidelines]: https://rust-lang.github.io/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_` and `wrapping_` operations on integers -(the `` part being `add`, `mul`, `sub`, `shr`, etc.). - -```rust -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 {{#check LANG-ARITH | Use appropriate arithmetic operations regarding potential overflows}}** -> -> When assuming that an arithmetic operation can produce an overflow, the -> specialized functions `overflowing_`, `wrapping_`, 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 {{#check LANG-ERRWRAP | Implement custom `Error` type, wrapping all possible errors}}** -> -> 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 {{#check LANG-ERRDO | Use the `?` operator and do not use the `try!` macro}}** -> -> 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. - -[failure]: https://crates.io/crates/failure -[snafu]: https://crates.io/crates/snafu -[thiserror]: https://crates.io/crates/thiserror -[anyhow]: https://crates.io/crates/anyhow - -### 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 {{#check LANG-NOPANIC | Don't use functions that can cause `panic!`}}** -> -> Functions or instructions that can cause the code to panic at runtime must not -> be used. - -> **Rule {{#check LANG-ARRINDEXING | Test properly array indexing or use the `get` method}}** -> -> 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 {{#check LANG-FFIPANIC | Handle correctly `panic!` in FFI}}** -> -> 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. - - - - diff --git a/src/en/04_standard.md b/src/en/04_standard.md new file mode 100644 index 0000000..e2f23ca --- /dev/null +++ b/src/en/04_standard.md @@ -0,0 +1,234 @@ +# Standard library + +## `Send` and `Sync` traits + +The `Send` and `Sync` traits (defined in `std::marker` or `core::marker`) are +marker traits used to ensure the safety of concurrency in Rust. When implemented +correctly, they allow the Rust compiler to guarantee the absence of data races. +Their semantics is as follows: + +- A type is `Send` if it is safe to send (move) it to another thread. +- A type is `Sync` if it is safe to share a immutable reference to it with + another thread. + +Both traits are _unsafe traits_, i.e., the Rust compiler does not verify in any +way that they are implemented correctly. The danger is real: an incorrect +implementation may lead to **undefined behavior**. + +Fortunately, in most cases, one does not need to implement it. In Rust, +almost all primitive types are `Send` and `Sync`, and for most compound types +the implementation is automatically provided by the Rust compiler. +Notable exceptions are: + +- Raw pointers are neither `Send` nor `Sync` because they offer no safety + guards. +- `UnsafeCell` is not `Sync` (and as a result `Cell` and `RefCell` aren't + either) because they offer interior mutability (mutably shared value). +- `Rc` is neither `Send` nor `Sync` because the reference counter is shared and + unsynchronized. + +Automatic implementation of `Send` (resp. `Sync`) occurs for a compound type +(structure or enumeration) when all fields have `Send` types (resp. `Sync` +types). Using an unstable feature (as of Rust 1.37.0), one can block the +automatic implementation of those traits with a manual +_negative implementation_: + +```rust,ignore,noplaypen +#![feature(option_builtin_traits)] + +struct SpecialType(u8); +impl !Send for SpecialType {} +impl !Sync for SpecialType {} +``` + +The negative implementation of `Send` or `Sync` are also used in the standard +library for the exceptions, and are automatically implemented when appropriate. +As a result, the generated documentation is always explicit: a type implements +either `Send` or `!Send` (resp. `Sync` or `!Sync`). + +As a stable alternative to negative implementation, one can use a `PhantomData` +field: + +```rust,noplaypen +# use std::marker::PhantomData; +# +struct SpecialType(u8, PhantomData<*const ()>); +``` + +> **Recommendation {{#check LANG-SYNC-TRAITS | Justify `Send` and `Sync` implementation}}** +> +> In a Rust secure development, the manual implementation of the `Send` and +> `Sync` traits should be avoided and, if necessary, should be justified, +> documented and peer-reviewed. + +## Comparison traits (`PartialEq`, `Eq`, `PartialOrd`, `Ord`) + +Comparisons (`==`, `!=`, `<`, `<=`, `>`, `>=`) in Rust relies on four standard +traits available in `std::cmp` (or `core::cmp` for `no_std` compilation): + +- `PartialEq` that defines a partial equivalence between + objects of types `Self` and `Rhs`, +- `PartialOrd` that defines a partial order between objects of types + `Self` and `Rhs`, +- `Eq` that defines a total equivalence between objects of the same + type. It is only a marker trait that requires `PartialEq`! +- `Ord` that defines the total order between objects of the same type. + It requires that `PartialOrd` is implemented. + +As documented in the standard library, Rust assumes **a lot of invariants** +about the implementations of those traits: + +- For `PartialEq` + + - *Internal consistency*: `a.ne(b)` is equivalent to `!a.eq(b)`, i.e., `ne` is + the strict inverse of `eq`. The default implementation of `ne` is precisely + that. + + - *Symmetry*: `a.eq(b)` and `b.eq(a)`, are equivalent. From the developer's + point of view, it means: + + - `PartialEq` is implemented for type `A` (noted `A: PartialEq`), + - `PartialEq` is implemented for type `B` (noted `B: PartialEq`), + - both implementations are consistent with each other. + + - *Transitivity*: `a.eq(b)` and `b.eq(c)` implies `a.eq(c)`. It means that: + + - `A: PartialEq`, + - `B: PartialEq`, + - `A: PartialEq`, + - the three implementations are consistent with each other (and their + symmetric implementations). + +- For `Eq` + + - `PartialEq` is implemented. + + - *Reflexivity*: `a.eq(a)`. This stands for `PartialEq` (`Eq` does not + provide any method). + +- For `PartialOrd` + + - *Equality consistency*: + `a.eq(b)` is equivalent to `a.partial_cmp(b) == Some(std::ordering::Eq)`. + + - *Internal consistency*: + + - `a.lt(b)` iff `a.partial_cmp(b) == Some(std::ordering::Less)`, + - `a.gt(b)` iff `a.partial_cmp(b) == Some(std::ordering::Greater)`, + - `a.le(b)` iff `a.lt(b) || a.eq(b)`, + - `a.ge(b)` iff `a.gt(b) || a.eq(b)`. + + Note that by only defining `partial_cmp`, the internal consistency is + guaranteed by the default implementation of `lt`, `le`, `gt`, and `ge`. + + - *Antisymmetry*: `a.lt(b)` (respectively `a.gt(b)`) implies `b.gt(a)` + (respectively, `b.lt(b)`). From the developer's standpoint, it also means: + + - `A: PartialOrd`, + - `B: PartialOrd`, + - both implementations are consistent with each other. + + - *Transitivity*: `a.lt(b)` and `b.lt(c)` implies `a.lt(c)` (also with `gt`, + `le` and `ge`). It also means: + + - `A: PartialOrd`, + - `B: PartialOrd`, + - `A: PartialOrd`, + - the implementations are consistent with each other (and their symmetric). + +- For `Ord` + + - `PartialOrd` + + - *Totality*: `a.partial_cmp(b) != None` always. In other words, + exactly one of `a.eq(b)`, `a.lt(b)`, `a.gt(b)` is true. + + - *Consistency with `PartialOrd`*: `Some(a.cmp(b)) == a.partial_cmp(b)`. + +The compiler do not check any of those requirements except for the type checking +itself. However, comparisons are critical because they intervene both in +liveness critical systems such as schedulers and load balancers, and in +optimized algorithms that may use `unsafe` blocks. +In the first use, a bad ordering may lead to availability issues such as +deadlocks. +In the second use, it may lead to classical security issues linked to memory +safety violations. That is again a factor in the practice of limiting the use +of `unsafe` blocks. + +> **Rule {{#check LANG-CMP-INV | Respect the invariants of standard comparison traits}}** +> +> In a Rust secure development, the implementation of standard comparison traits +> must respect the invariants described in the documentation. + +> **Recommendation {{#check LANG-CMP-DEFAULTS | Use the default method implementation of standard comparison traits}}** +> +> In a Rust secure development, the implementation of standard comparison traits +> should only define methods with no default implementation, so as to reduce +> the risk of violating the invariants associated with the traits. + +There is a Clippy lint to check that `PartialEq::ne` is not defined in +`PartialEq` implementations. + +Rust comes with a standard way to automatically construct implementations of the +comparison traits through the `#[derive(...)]` attribute: + +- Derivation `PartialEq` implements `PartialEq` with a + **structural equality** providing that each subtype is `PartialEq`. +- Derivation `Eq` implements the `Eq` marker trait providing that each subtype + is `Eq`. +- Derivation `PartialOrd` implements `PartialOrd` as a + **lexicographical order** providing that each subtype is `PartialOrd`. +- Derivation `Ord` implements `Ord` as a **lexicographical order** + providing that each subtype is `Ord`. + +For instance, the short following code shows how to compare two `T1`s easily. +All the assertions hold. + +```rust +#[derive(PartialEq, Eq, PartialOrd, Ord)] +struct T1 { + a: u8, b: u8 +} + +# fn main() { +assert!(&T1 { a: 0, b: 0 } == Box::new(T1 { a: 0, b: 0 }).as_ref()); +assert!(T1 { a: 1, b: 0 } > T1 { a: 0, b: 0 }); +assert!(T1 { a: 1, b: 1 } > T1 { a: 1, b: 0 }); +# println!("all tests passed."); +# } +``` + +> **Warning** +> +> Derivation of comparison traits for compound types depends on the +> **field order**, and not on their names. +> +> First, it means that changing the order of declaration of two fields change +> the resulting lexicographical order. For instance, provided this second +> ordered type: +> +> ```rust,noplaypen +> #[derive(PartialEq, Eq, PartialOrd, Ord)] +> struct T2{ +> b: u8, a: u8 +> }; +> ``` +> +> we have `T1 {a: 1, b: 0} > T1 {a: 0, b: 1}` but +> `T2 {a: 1, b: 0} < T2 {a: 0, b: 1}`. +> +> Second, if one of the underlying comparison panics, the order may change the +> result due to the use of short-circuit logic in the automatic implementation. +> +> For enums, the derived comparisons depends first on the **variant order** then +> on the field order. + +Despite the ordering caveat, derived comparisons are a lot less error-prone +than manual ones and makes code shorter and easier to maintain. + +> **Recommendation {{#check LANG-CMP-DERIVE | Derive comparison traits when possible}}** +> +> In a secure Rust development, the implementation of standard comparison traits +> should be automatically derived with `#[derive(...)]` when structural equality +> and lexicographical comparison is needed. Any manual implementation of +> standard comparison traits should be documented and justified. diff --git a/src/en/05_central_traits.md b/src/en/05_central_traits.md new file mode 100644 index 0000000..eaa43c6 --- /dev/null +++ b/src/en/05_central_traits.md @@ -0,0 +1,59 @@ +# Central traits + +## `Drop` trait, the destructor + +Types implement the trait `std::ops::Drop` to perform some operations when the +memory associated with a value of this type is to be reclaimed. `Drop` is the +Rust equivalent of a destructor in C++ or a finalizer in Java. + +Dropping is done recursively from the outer value to the inner values. +When a value goes out of scope (or is explicitly dropped with `std::mem::drop`), +the value is dropped in two steps. The first step happens only if the type of +this value implements `Drop`. It consists in calling the `drop` method on it. +The second step consists in repeating the dropping process recursively on any +field the value contains. Note that a `Drop` implementation is +**only responsible for the outer value**. + +First and foremost, implementing `Drop` should not be systematic. +It is only needed if the type requires some destructor logic. In fact, `Drop` is +typically used to release external resources (network connections, files, etc.) +or to release memory (e.g. in smart pointers such as `Box` or `Rc`). +As a result, `Drop` trait implementations are likely to contain `unsafe` code +blocks as well as other security-critical operations. + +> **Recommendation {{#check LANG-DROP | Justify `Drop` implementation}}** +> +> In a Rust secure development, the implementation of the `std::ops::Drop` trait +> should be justified, documented and peer-reviewed. + +Second, Rust type system only ensures memory safety and, from the type system's +standpoint, missing drops is allowed. In fact, several things may lead to +missing drops, such as: + +- a reference cycle (for instance, with `Rc` or `Arc`), +- an explicit call to `std::mem::forget` (or `core::mem::forget`) (see paragraph + on [Forget and memory leaks](05_memory.html#forget-and-memory-leaks), +- a panic in drop, +- program aborts (and panics when abort-on-panic is on). + + +And missing drops may lead to exposing sensitive data or to lock limited +resources leading to unavailability issues. + +> **Rule {{#check LANG-DROP-NO-PANIC | Do not panic in `Drop` implementation}}** +> +> In a Rust secure development, the implementation of the `std::ops::Drop` trait +> must not panic. + +Beside panics, secure-critical drop should be protected. + +> **Rule {{#check LANG-DROP-NO-CYCLE | Do not allow cycles of reference-counted `Drop`}}** +> +> Value whose type implements `Drop` must not be embedded directly or indirectly +> in a cycle of reference-counted references. + +> **Recommendation {{#check LANG-DROP-SEC | Do not rely only on `Drop` to ensure security}}** +> +> Ensuring security operations at the end of some treatment (such as key erasure +> at the end of a cryptographic encryption) should not rely only on the `Drop` +> trait implementation. diff --git a/src/en/05_errors.md b/src/en/05_errors.md new file mode 100644 index 0000000..7635851 --- /dev/null +++ b/src/en/05_errors.md @@ -0,0 +1,86 @@ +# 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 {{#check LANG-ERRWRAP | Implement custom `Error` type, wrapping all possible errors}}** +> +> 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 {{#check LANG-ERRDO | Use the `?` operator and do not use the `try!` macro}}** +> +> 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. + +[failure]: https://crates.io/crates/failure +[snafu]: https://crates.io/crates/snafu +[thiserror]: https://crates.io/crates/thiserror +[anyhow]: https://crates.io/crates/anyhow + +## 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 {{#check LANG-NOPANIC | Don't use functions that can cause `panic!`}}** +> +> Functions or instructions that can cause the code to panic at runtime must not +> be used. + +> **Rule {{#check LANG-ARRINDEXING | Test properly array indexing or use the `get` method}}** +> +> 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 {{#check LANG-FFIPANIC | Handle correctly `panic!` in FFI}}** +> +> 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. diff --git a/src/en/05_guarantees.md b/src/en/05_guarantees.md new file mode 100644 index 0000000..8a3d905 --- /dev/null +++ b/src/en/05_guarantees.md @@ -0,0 +1,33 @@ +# 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](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.general). + +For example, dereferencing the null pointer is a *UB*. On the other hand, `unwrap`ing 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](https://doc.rust-lang.org/reference/behavior-considered-undefined.html). 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](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.invalid) 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](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.alias) (see also [nomicon](https://doc.rust-lang.org/nomicon/aliasing.html)): a mutable reference cannot be shared. +* No concurrent access (reading/writing is not possible while writing) to the same memory address ([data race](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.race), see also [nomicon](https://doc.rust-lang.org/nomicon/races.html)) + +## 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 diff --git a/src/en/05_integer.md b/src/en/05_integer.md new file mode 100644 index 0000000..e8594ee --- /dev/null +++ b/src/en/05_integer.md @@ -0,0 +1,48 @@ +# Integer operations + +## 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_` and `wrapping_` operations on integers +(the `` part being `add`, `mul`, `sub`, `shr`, etc.). + +```rust +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 {{#check LANG-ARITH | Use appropriate arithmetic operations regarding potential overflows}}** +> +> When assuming that an arithmetic operation can produce an overflow, the +> specialized functions `overflowing_`, `wrapping_`, or the +> `Wrapping` type must be used. diff --git a/src/en/05_macros.md b/src/en/05_macros.md new file mode 100644 index 0000000..509cd31 --- /dev/null +++ b/src/en/05_macros.md @@ -0,0 +1,8 @@ +# Macros + + + + \ No newline at end of file diff --git a/src/en/05_naming.md b/src/en/05_naming.md new file mode 100644 index 0000000..07d8dcc --- /dev/null +++ b/src/en/05_naming.md @@ -0,0 +1,30 @@ +# 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 {{#check LANG-NAMING | Respect naming conventions}}** +> +> Development of a secure application must follow the naming conventions +> outlined in the [Rust API Guidelines]. + +[rfc 430]: https://github.com/rust-lang/rfcs/blob/master/text/0430-finalizing-naming-conventions.md +[rust api guidelines]: https://rust-lang.github.io/api-guidelines/ diff --git a/src/en/05_typesystem.md b/src/en/05_typesystem.md index 7b63154..c375b06 100644 --- a/src/en/05_typesystem.md +++ b/src/en/05_typesystem.md @@ -8,295 +8,3 @@ misunderstanding of which code is actually executed when implementing complex patterns with traits). --> -## Standard library traits - -### `Drop` trait, the destructor - -Types implement the trait `std::ops::Drop` to perform some operations when the -memory associated with a value of this type is to be reclaimed. `Drop` is the -Rust equivalent of a destructor in C++ or a finalizer in Java. - -Dropping is done recursively from the outer value to the inner values. -When a value goes out of scope (or is explicitly dropped with `std::mem::drop`), -the value is dropped in two steps. The first step happens only if the type of -this value implements `Drop`. It consists in calling the `drop` method on it. -The second step consists in repeating the dropping process recursively on any -field the value contains. Note that a `Drop` implementation is -**only responsible for the outer value**. - -First and foremost, implementing `Drop` should not be systematic. -It is only needed if the type requires some destructor logic. In fact, `Drop` is -typically used to release external resources (network connections, files, etc.) -or to release memory (e.g. in smart pointers such as `Box` or `Rc`). -As a result, `Drop` trait implementations are likely to contain `unsafe` code -blocks as well as other security-critical operations. - -> **Recommendation {{#check LANG-DROP | Justify `Drop` implementation}}** -> -> In a Rust secure development, the implementation of the `std::ops::Drop` trait -> should be justified, documented and peer-reviewed. - -Second, Rust type system only ensures memory safety and, from the type system's -standpoint, missing drops is allowed. In fact, several things may lead to -missing drops, such as: - -- a reference cycle (for instance, with `Rc` or `Arc`), -- an explicit call to `std::mem::forget` (or `core::mem::forget`) (see paragraph - on [Forget and memory leaks](05_memory.html#forget-and-memory-leaks), -- a panic in drop, -- program aborts (and panics when abort-on-panic is on). - - -And missing drops may lead to exposing sensitive data or to lock limited -resources leading to unavailability issues. - -> **Rule {{#check LANG-DROP-NO-PANIC | Do not panic in `Drop` implementation}}** -> -> In a Rust secure development, the implementation of the `std::ops::Drop` trait -> must not panic. - -Beside panics, secure-critical drop should be protected. - -> **Rule {{#check LANG-DROP-NO-CYCLE | Do not allow cycles of reference-counted `Drop`}}** -> -> Value whose type implements `Drop` must not be embedded directly or indirectly -> in a cycle of reference-counted references. - -> **Recommendation {{#check LANG-DROP-SEC | Do not rely only on `Drop` to ensure security}}** -> -> Ensuring security operations at the end of some treatment (such as key erasure -> at the end of a cryptographic encryption) should not rely only on the `Drop` -> trait implementation. - -### `Send` and `Sync` traits - -The `Send` and `Sync` traits (defined in `std::marker` or `core::marker`) are -marker traits used to ensure the safety of concurrency in Rust. When implemented -correctly, they allow the Rust compiler to guarantee the absence of data races. -Their semantics is as follows: - -- A type is `Send` if it is safe to send (move) it to another thread. -- A type is `Sync` if it is safe to share a immutable reference to it with - another thread. - -Both traits are _unsafe traits_, i.e., the Rust compiler does not verify in any -way that they are implemented correctly. The danger is real: an incorrect -implementation may lead to **undefined behavior**. - -Fortunately, in most cases, one does not need to implement it. In Rust, -almost all primitive types are `Send` and `Sync`, and for most compound types -the implementation is automatically provided by the Rust compiler. -Notable exceptions are: - -- Raw pointers are neither `Send` nor `Sync` because they offer no safety - guards. -- `UnsafeCell` is not `Sync` (and as a result `Cell` and `RefCell` aren't - either) because they offer interior mutability (mutably shared value). -- `Rc` is neither `Send` nor `Sync` because the reference counter is shared and - unsynchronized. - -Automatic implementation of `Send` (resp. `Sync`) occurs for a compound type -(structure or enumeration) when all fields have `Send` types (resp. `Sync` -types). Using an unstable feature (as of Rust 1.37.0), one can block the -automatic implementation of those traits with a manual -_negative implementation_: - -```rust,ignore,noplaypen -#![feature(option_builtin_traits)] - -struct SpecialType(u8); -impl !Send for SpecialType {} -impl !Sync for SpecialType {} -``` - -The negative implementation of `Send` or `Sync` are also used in the standard -library for the exceptions, and are automatically implemented when appropriate. -As a result, the generated documentation is always explicit: a type implements -either `Send` or `!Send` (resp. `Sync` or `!Sync`). - -As a stable alternative to negative implementation, one can use a `PhantomData` -field: - -```rust,noplaypen -# use std::marker::PhantomData; -# -struct SpecialType(u8, PhantomData<*const ()>); -``` - -> **Recommendation {{#check LANG-SYNC-TRAITS | Justify `Send` and `Sync` implementation}}** -> -> In a Rust secure development, the manual implementation of the `Send` and -> `Sync` traits should be avoided and, if necessary, should be justified, -> documented and peer-reviewed. - -### Comparison traits (`PartialEq`, `Eq`, `PartialOrd`, `Ord`) - -Comparisons (`==`, `!=`, `<`, `<=`, `>`, `>=`) in Rust relies on four standard -traits available in `std::cmp` (or `core::cmp` for `no_std` compilation): - -- `PartialEq` that defines a partial equivalence between - objects of types `Self` and `Rhs`, -- `PartialOrd` that defines a partial order between objects of types - `Self` and `Rhs`, -- `Eq` that defines a total equivalence between objects of the same - type. It is only a marker trait that requires `PartialEq`! -- `Ord` that defines the total order between objects of the same type. - It requires that `PartialOrd` is implemented. - -As documented in the standard library, Rust assumes **a lot of invariants** -about the implementations of those traits: - -- For `PartialEq` - - - *Internal consistency*: `a.ne(b)` is equivalent to `!a.eq(b)`, i.e., `ne` is - the strict inverse of `eq`. The default implementation of `ne` is precisely - that. - - - *Symmetry*: `a.eq(b)` and `b.eq(a)`, are equivalent. From the developer's - point of view, it means: - - - `PartialEq` is implemented for type `A` (noted `A: PartialEq`), - - `PartialEq` is implemented for type `B` (noted `B: PartialEq`), - - both implementations are consistent with each other. - - - *Transitivity*: `a.eq(b)` and `b.eq(c)` implies `a.eq(c)`. It means that: - - - `A: PartialEq`, - - `B: PartialEq`, - - `A: PartialEq`, - - the three implementations are consistent with each other (and their - symmetric implementations). - -- For `Eq` - - - `PartialEq` is implemented. - - - *Reflexivity*: `a.eq(a)`. This stands for `PartialEq` (`Eq` does not - provide any method). - -- For `PartialOrd` - - - *Equality consistency*: - `a.eq(b)` is equivalent to `a.partial_cmp(b) == Some(std::ordering::Eq)`. - - - *Internal consistency*: - - - `a.lt(b)` iff `a.partial_cmp(b) == Some(std::ordering::Less)`, - - `a.gt(b)` iff `a.partial_cmp(b) == Some(std::ordering::Greater)`, - - `a.le(b)` iff `a.lt(b) || a.eq(b)`, - - `a.ge(b)` iff `a.gt(b) || a.eq(b)`. - - Note that by only defining `partial_cmp`, the internal consistency is - guaranteed by the default implementation of `lt`, `le`, `gt`, and `ge`. - - - *Antisymmetry*: `a.lt(b)` (respectively `a.gt(b)`) implies `b.gt(a)` - (respectively, `b.lt(b)`). From the developer's standpoint, it also means: - - - `A: PartialOrd`, - - `B: PartialOrd`, - - both implementations are consistent with each other. - - - *Transitivity*: `a.lt(b)` and `b.lt(c)` implies `a.lt(c)` (also with `gt`, - `le` and `ge`). It also means: - - - `A: PartialOrd`, - - `B: PartialOrd`, - - `A: PartialOrd`, - - the implementations are consistent with each other (and their symmetric). - -- For `Ord` - - - `PartialOrd` - - - *Totality*: `a.partial_cmp(b) != None` always. In other words, - exactly one of `a.eq(b)`, `a.lt(b)`, `a.gt(b)` is true. - - - *Consistency with `PartialOrd`*: `Some(a.cmp(b)) == a.partial_cmp(b)`. - -The compiler do not check any of those requirements except for the type checking -itself. However, comparisons are critical because they intervene both in -liveness critical systems such as schedulers and load balancers, and in -optimized algorithms that may use `unsafe` blocks. -In the first use, a bad ordering may lead to availability issues such as -deadlocks. -In the second use, it may lead to classical security issues linked to memory -safety violations. That is again a factor in the practice of limiting the use -of `unsafe` blocks. - -> **Rule {{#check LANG-CMP-INV | Respect the invariants of standard comparison traits}}** -> -> In a Rust secure development, the implementation of standard comparison traits -> must respect the invariants described in the documentation. - -> **Recommendation {{#check LANG-CMP-DEFAULTS | Use the default method implementation of standard comparison traits}}** -> -> In a Rust secure development, the implementation of standard comparison traits -> should only define methods with no default implementation, so as to reduce -> the risk of violating the invariants associated with the traits. - -There is a Clippy lint to check that `PartialEq::ne` is not defined in -`PartialEq` implementations. - -Rust comes with a standard way to automatically construct implementations of the -comparison traits through the `#[derive(...)]` attribute: - -- Derivation `PartialEq` implements `PartialEq` with a - **structural equality** providing that each subtype is `PartialEq`. -- Derivation `Eq` implements the `Eq` marker trait providing that each subtype - is `Eq`. -- Derivation `PartialOrd` implements `PartialOrd` as a - **lexicographical order** providing that each subtype is `PartialOrd`. -- Derivation `Ord` implements `Ord` as a **lexicographical order** - providing that each subtype is `Ord`. - -For instance, the short following code shows how to compare two `T1`s easily. -All the assertions hold. - -```rust -#[derive(PartialEq, Eq, PartialOrd, Ord)] -struct T1 { - a: u8, b: u8 -} - -# fn main() { -assert!(&T1 { a: 0, b: 0 } == Box::new(T1 { a: 0, b: 0 }).as_ref()); -assert!(T1 { a: 1, b: 0 } > T1 { a: 0, b: 0 }); -assert!(T1 { a: 1, b: 1 } > T1 { a: 1, b: 0 }); -# println!("all tests passed."); -# } -``` - -> **Warning** -> -> Derivation of comparison traits for compound types depends on the -> **field order**, and not on their names. -> -> First, it means that changing the order of declaration of two fields change -> the resulting lexicographical order. For instance, provided this second -> ordered type: -> -> ```rust,noplaypen -> #[derive(PartialEq, Eq, PartialOrd, Ord)] -> struct T2{ -> b: u8, a: u8 -> }; -> ``` -> -> we have `T1 {a: 1, b: 0} > T1 {a: 0, b: 1}` but -> `T2 {a: 1, b: 0} < T2 {a: 0, b: 1}`. -> -> Second, if one of the underlying comparison panics, the order may change the -> result due to the use of short-circuit logic in the automatic implementation. -> -> For enums, the derived comparisons depends first on the **variant order** then -> on the field order. - -Despite the ordering caveat, derived comparisons are a lot less error-prone -than manual ones and makes code shorter and easier to maintain. - -> **Recommendation {{#check LANG-CMP-DERIVE | Derive comparison traits when possible}}** -> -> In a secure Rust development, the implementation of standard comparison traits -> should be automatically derived with `#[derive(...)]` when structural equality -> and lexicographical comparison is needed. Any manual implementation of -> standard comparison traits should be documented and justified. diff --git a/src/en/SUMMARY.md b/src/en/SUMMARY.md index 7ec884f..7d67f57 100644 --- a/src/en/SUMMARY.md +++ b/src/en/SUMMARY.md @@ -1,15 +1,30 @@ # Summary -- [Introduction](01_introduction.md) +[Introduction](01_introduction.md) + +# Lifecycle + - [Development environment](02_devenv.md) - [Libraries](03_libraries.md) -- [Language generalities](04_language.md) -- [Type system](05_typesystem.md) + +# Language + +- [Language guarantees](05_guarantees.md) +- [Naming](05_naming.md) + +- [Integer operations](05_integer.md) +- [Error handling](05_errors.md) + +- [Central traits](05_central_traits.md) - [Unsafe Rust](06_unsafe.md) - [Generalities](06_1_unsafe_generalities.md) - [Memory management](06_2_unsafe_memory.md) - [Foreign Function Interface](06_3_unsafe_ffi.md) +# Ecosystem + +- [Standard library](04_standard.md) + [Licence](LICENCE.md) diff --git a/src/fr/04_language.md b/src/fr/04_language.md deleted file mode 100644 index 7d887bb..0000000 --- a/src/fr/04_language.md +++ /dev/null @@ -1,225 +0,0 @@ -# Généralités sur le langage - -## Garanties du langage - -### Comportements indéfinis - -> Le comportement d'un programme est *indéfini* (*UB* pour *Undefined Behavior*) lorsque sa sémantique n'est -> pas décrite dans le langage Rust. - -L'existence d'*UB* est considéré comme une [erreur](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.general). - -Par exemple le déréférencement d'un pointeur null est un *UB*. -*A contrario*, un `unwrap` sur l'objet `None` est bien *défini* car c'est le langage qui traite cette erreur -(en lançant un `panic`). - -La liste actuelle des *UB* est donnée [ici](https://doc.rust-lang.org/reference/behavior-considered-undefined.html). -On notera les garanties suivantes : - -* Pas de déréférencement de pointeur vers une adresse mémoire non allouée (*dangling pointer*) ou non alignée, ce qui implique - * Pas de dépassement de tableau - * Pas d'accès à de la mémoire libérée - * Accès toujours aligné quelque soit la plateforme -* Les valeurs pointées sont [cohérentes](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.invalid) avec le type du pointeur. Par exemple, une valeur pointée par un pointeur booléen sera l'octet 1 ou 0. -* Respect des règles d'[*aliasing*](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.alias) (voir aussi le [nomicon](https://doc.rust-lang.org/nomicon/aliasing.html)): une référence mutable ne peux être partagée. -* Pas d'accès concurrent (un accès en lecture et un autre en écriture ou en lecture) à la même adresse mémoire ([*data race*](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.race), voir aussi le [nomicon](https://doc.rust-lang.org/nomicon/races.html)) - -### Garantie de Rust - -> La volonté du langage est d'assurer l'absence d'*UB* dans un programme utilisant uniquement la partie non *unsafe* de Rust. - -Cependant, le langage ***ne protège pas*** contre les erreurs suivantes : - -* fuites de resources (mémoire, IO, ...) ; -* dépassements numériques. - -### Références - -* https://doc.rust-lang.org/reference/unsafety.html -* https://doc.rust-lang.org/nomicon/what-unsafe-does.html - - -## Nommage - -La convention de nommage employée par la bibliothèque standard est *de facto* le -standard pour le nommage des éléments des programmes écrits en Rust. Un effort a -été fait pour formaliser ces conventions de nommage, d'abord dans la [RFC 430], -puis dans le document des *[Rust API Guidelines]*. - -La règle de base consiste à utiliser : - -- `UpperCamelCase` pour les types, traits et valeurs d'énumérations ; -- `snake_case` pour les fonctions, méthodes, macros, variables et modules ; -- `SCREAMING_SNAKE_CASE` pour les variables statiques et les constantes ; -- `'lowercase` pour les durées de vie (*lifetimes*). - -Les [Rust API Guidelines] recommandent également des conventions de nommage -plus précises pour certaines constructions particulières : - -- (C-CONV) pour les méthodes de conversion (`as_`, `to_`, `into_`) ; -- (C-GETTER) pour les accesseurs ; -- (C-ITER) pour les méthodes produisant des itérateurs ; -- (C-ITER-TY) pour les types itérateur ; -- (C-FEATURE) pour les noms de *features* ; -- (C-WORD-ORDER) pour la cohérence sur l'ordre des mots. - -> **Règle {{#check LANG-NAMING | Respect des conventions de nommage}}** -> -> Le développement d'une application sécurisée doit suivre les conventions de -> nommage décrites dans les [Rust API Guidelines]. - -[rfc 430]: https://github.com/rust-lang/rfcs/blob/master/text/0430-finalizing-naming-conventions.md -[rust api guidelines]: https://rust-lang.github.io/api-guidelines/ - - - -## Dépassement d'entiers - -Bien que des vérifications soient effectuées par Rust en ce qui concerne les -potentiels dépassements d'entiers, des précautions doivent être prises lors de -l'exécution d'opérations arithmétiques sur les entiers. - -En particulier, il doit être noté que les profils de compilation *debug* et -*release* produisent des variations de comportements quant à la gestion des -dépassements d'entiers. Dans la configuration *debug*, un dépassement provoque -la terminaison du programme (`panic`), tandis que dans la configuration -*release* la valeur calculée est silencieusement tronquée en fonction de la -valeur maximum qui peut être stockée pour le type considéré. - -Ce comportement peut être rendu explicite en utilisant le type générique -`Wrapping`, ou les opérations sur les entiers `overflowing_` et -`wrapping_` (la partie `` étant remplacée par le type de calcul : -`add`, `mul`, `sub`, `shr`, etc.). - -```rust -use std::num::Wrapping; -# use std::panic; - -# fn main() { -let x: u8 = 242; - -# let result = panic::catch_unwind(|| { -println!("{}", x + 50); // panique en mode debug, affiche 36 en mode release. -# }); -# if result.is_err() { println!("panic"); } -println!("{}", x.overflowing_add(50).0); // affiche toujours 36. -println!("{}", x.wrapping_add(50)); // affiche toujours 36. -println!("{}", Wrapping(x) + Wrapping(50)); // affiche toujours 36. - -// panique toujours : -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"); } -# } -``` - -> **Règle {{#check LANG-ARITH | Utilisation des opérations arithmétiques appropriées au regard des potentiels dépassements}}** -> -> Lorsqu'une opération arithmétique peut produire un dépassement d'entier, les -> fonctions spécialisées `overflowing_`, `wrapping_` ou le type -> `Wrapping` doivent être utilisés. - -## Gestion des erreurs - - - -Le type `Result` est la façon privilégiée en Rust pour décrire le type de retour -des fonctions dont le traitement peut échouer. Un objet `Result` doit être -testé et jamais ignoré. - -> **Recommandation {{#check LANG-ERRWRAP | Mise en place d'un type `Error` personnalisé, pouvant contenir toutes les erreurs possibles}}** -> -> Une *crate* peut implanter son propre type `Error` qui peut contenir toutes -> les erreurs possibles. Des précautions supplémentaires doivent être prises : -> ce type doit être *exception-safe* (RFC 1236) et implémenter les traits -> `Error + Send + Sync + 'static` ainsi que `Display`. - -> **Recommandation {{#check LANG-ERRDO | Utilisation de l'opérateur `?` et non-utilisation de la macro `try!`}}** -> -> L'opérateur `?` doit être utilisé pour améliorer la lisibilité du code. -> La macro `try!` ne doit pas être utilisée. - -Des *crates* tierces peuvent être utilisées pour faciliter la gestion d'erreurs. -La plupart ([failure], [snafu], [thiserror]) proposent la création de types -d'erreurs personnalisées qui implémentent les traits nécessaires et permettent -l'encapsulation d'autres erreurs. - -Une autre approche (notamment proposée dans [anyhow]) consiste à envelopper -automatiquement les erreurs dans un seul type d'erreur universel. Une telle -approche ne devrait pas être utilisée dans des bibliothèques ou des systèmes -complexes parce qu'elle ne permet pas de fournir de contexte sur les erreurs -ainsi initialement enveloppées, contrairement à la première approche. - -[failure]: https://crates.io/crates/failure -[snafu]: https://crates.io/crates/snafu -[thiserror]: https://crates.io/crates/thiserror -[anyhow]: https://crates.io/crates/anyhow - -### *Panics* - -La gestion explicite des erreurs (`Result`) doit être préférée à la place de -l'utilisation de la macro `panic`. La cause de l'erreur doit être rendue -disponible, et les erreurs trop génériques doivent être évitées. - -Les *crates* fournissant des bibliothèques ne doivent pas utiliser de fonctions -ou d'instructions qui peuvent échouer en engendrant un `panic`. - -Des motifs courants de code qui provoquent des `panic` sont : - -- une utilisation de `unwrap` ou de `expect` ; -- une utilisation de `assert` ; -- un accès non vérifié à un tableau ; -- un dépassement d'entier (en mode *debug*) ; -- une division par zéro ; -- l'utilisation de `format!` pour le formatage d'une chaîne de caractères. - -> **Règle {{#check LANG-NOPANIC | Non-utilisation de fonctions qui peuvent causer des `panic`}}** -> -> Les fonctions et instructions qui peuvent causer des `panic` à l'exécution -> ne doivent pas être utilisées. - -> **Règle {{#check LANG-ARRINDEXING | Test des indices d'accès aux tableaux ou utilisation de la méthode `get`}}** -> -> L'indice d'accès à un tableau doit être testé, ou la méthode `get` doit être -> utilisée pour récupérer une `Option`. - - - - -### FFI et `panic`s - -Lorsque du code Rust est appelé depuis du code écrit dans un autre -langage (par exemple, du code C), le code Rust doit être écrit de sorte à ne -jamais pouvoir paniquer. -Dérouler (*unwinding*) depuis le code Rust vers le code étranger résulte en un -comportement indéfini. - -> **Règle {{#check LANG-FFIPANIC | Gestion correcte des `panic!` dans les FFI}}** -> -> Le code Rust appelé depuis une FFI doit soit être assuré de ne pas paniquer, -> soit utiliser `catch_unwind` ou le module `std::panic` pour s'assurer qu'il -> ne va pas abandonner un traitement puis que l'exécution retourne dans le -> langage appelant dans un état instable. - -Il est porté à l'attention du développeur que `catch_unwind` ne va traiter que -les cas de `panic`, et va préserver les abandons de processus causés par -d'autres raisons. - - - - diff --git a/src/fr/04_standard.md b/src/fr/04_standard.md new file mode 100644 index 0000000..697ea51 --- /dev/null +++ b/src/fr/04_standard.md @@ -0,0 +1,252 @@ +# Bibliothèque standard + +## Les traits `Send` et `Sync` + +Les traits `Send` et `Sync` (définis dans `std::marker` ou `core::marker`) sont +des marqueurs utilisés pour assurer la sûreté des accès concurrents en Rust. +Lorsqu'ils sont correctement implémentés, ils permettent au compilateur Rust de +garantir l'absence de problèmes d'accès concurrents. Leurs sémantiques sont +définies comme suit : + +- Un type est `Send` s’il est sûr d'envoyer (*move*) des valeurs de ce type vers + un autre fil d'exécution. +- Un type est `Sync` s’il est sûr de partager des valeurs de ce type par une + référence immutable avec un autre fil d'exécution. + +Ces deux traits sont des traits *unsafe*, c'est-à-dire que le compilateur Rust +ne vérifie d'aucune manière que leur implémentation est correcte. Le danger est +réel : une implémentation incorrecte peut mener à un **comportement indéfini**. + +Heureusement, dans la plupart des cas, il n'est pas nécessaire de fournir une +implémentation. En Rust, la quasi-totalité des types primitifs implémente +`Send` et `Sync`, et dans la majorité des cas, Rust fournit de manière +automatique une implémentation pour les types composés. Quelques exceptions +notables sont : + +- les pointeurs `raw`, qui n'implémentent ni `Send`, ni `Sync`, puisqu'ils + n'offrent aucune garantie quant à la sûreté ; +- les références `UnsafeCell`, qui n'implémentent pas `Sync` (et par extensions, + les références `Cell` et `RefCell` non plus), puisqu'elles autorisent la + mutabilité des valeurs contenues (*interior mutability*) ; +- les références `Rc`, qui n'implémentent ni `Send`, ni `Sync`, puisque les + compteurs de références seraient partagés de manière désynchronisée. + +L'implémentation automatique de `Send` (respectivement `Sync`) a lieu pour les +types composés (structures ou énumérations) lorsque tous les champs contenus +implémentent `Send` (respectivement `Sync`). Une fonctionnalité notable, mais +**instable**, de Rust (depuis 1.37.0) permet d'empêcher cette implémentation +automatique en annotant explicitement le type considéré avec une +_négation d'implementation_ : + +```rust,ignore,noplaypen +#![feature(option_builtin_traits)] + +struct SpecialType(u8); +impl !Send for SpecialType {} +impl !Sync for SpecialType {} +``` +L'implémentation négative de `Send` ou `Sync` est également utilisée dans la +bibliothèque standard pour les exceptions, et est automatiquement implémentée +lorsque cela est approprié. En résultat, la documentation générée est toujours +explicite : un type implémente soit `Send` (respectivement `Sync`), soit +`!Send` (respectivement `!Sync`). + +En guise d'alternative *stable* à l'implémentation négative, il est possible +d'utiliser un champ typé par un type fantôme (`PhantomData`) : + +```rust,noplaypen +# use std::marker::PhantomData; +# +struct SpecialType(u8, PhantomData<*const ()>); +``` + +> **Recommandation {{#check LANG-SYNC-TRAITS | Justification de l'implémentation des traits `Send` et `Sync`}}** +> +> Dans un développement sécurisé en Rust, l'implémentation manuelle des traits +> `Send` et `Sync` doit être évitée, et, si nécessaire, doit être justifiée, +> documentée et révisée par des pairs. + +## Les traits de comparaison : `PartialEq`, `Eq`, `PartialOrd`, `Ord` + +Les comparaisons (`==`, `!=`, `<`, `<=`, `>`, `>=`) en Rust reposent sur quatre +traits de la bibliothèque standard disponibles dans `std::cmp` (ou `core::cmp` +pour une compilation avec `no_std`) : + +- `PartialEq` qui définit la relation d'équivalence partielle entre objets + de types `Self` et `Rhs` ; +- `PartialOrd` qui définit la relation d'ordre partiel entre les objets de + types `Self` et `Rhs` ; +- `Eq` qui définit la relation d'équivalence totale entre les objets du même + type. Il s'agit d'un trait de marquage qui requiert le trait + `PartialEq` ; +- `Ord` qui définit la relation d'ordre total entre les objets du même type. + Le trait `PartialOrd` est alors requis. + +Comme stipulé dans la documentation de la bibliothèque standard, Rust présuppose +**de nombreux invariants** lors de l'implémentation de ces traits : + +- Pour `PartialEq` : + + - *Cohérence interne* : `a.ne(b)` est équivalent à `!a.eq(b)`, c.-à-d., `ne` + est le strict inverse de `eq`. Cela correspond précisément à + l'implémentation par défaut de `ne`. + + - *Symétrie* : `a.eq(b)` et `b.eq(a)` sont équivalents. Du point de vue du + développeur, cela signifie que : + + - `PartialEq` est implémenté pour le type `A` (noté `A: PartialEq`). + - `PartialEq` est implémenté pour le type `B` (noté `B: PartialEq`). + - Les deux implémentations sont cohérentes l'une avec l'autre. + + - *Transitivité* : `a.eq(b)` et `b.eq(c)` impliquent `a.eq(c)`. Cela signifie + que : + + - `A: PartialEq`. + - `B: PartialEq`. + - `A: PartialEq`. + - Les trois implémentations sont cohérentes les unes avec les autres (ainsi + qu'avec leurs implémentations symétriques). + +- Pour `Eq` : + + - `PartialEq` est implémenté. + + - *Réflexivité* : `a.eq(a)`. Cela signifie que `PartialEq` est + implémenté (`Eq` ne fournit aucune méthode). + +- Pour `PartialOrd` : + + - *Consistance de la relation d'égalité* : `a.eq(b)` est équivalent à + `a.partial_cmp(b) == Some(std::ordering::Eq)`. + + - *Consistence interne* : + + - `a.lt(b)` ssi `a.partial_cmp(b) == Some(std::ordering::Less)`. + - `a.gt(b)` ssi `a.partial_cmp(b) == Some(std::ordering::Greater)`. + - `a.le(b)` ssi `a.lt(b) || a.eq(b)`. + - `a.ge(b)` ssi `a.gt(b) || a.eq(b)`. + + Il faut noter qu'en définissant seulement `partial_cmp`, la consistance + interne est garantie par les implémentations par défaut de `lt`, `le`, `gt`, + and `ge`. + + - *Antisymétrie* : `a.lt(b)` (respectivement `a.gt(b)`) implique `b.gt(a)` + (respectivement `b.lt(b)`). Du point de vue du développeur, cela signifie + que : + + - `A: PartialOrd`. + - `B: PartialOrd`. + - Les deux implémentations sont cohérentes l'une avec l'autre. + + - *Transitivité* : `a.lt(b)` et `b.lt(c)` impliquent `a.lt(c)` (également avec + `gt`, `le` et `ge`). Cela signifie que : + + - `A: PartialOrd`. + - `B: PartialOrd`. + - `A: PartialOrd`. + - Les trois implémentations sont cohérentes les unes avec les autres (et + avec leurs implémentations symétriques). + +- Pour `Ord` : + + - `PartialOrd` + + - *Totalité* : `a.partial_cmp(b) != None` est toujours vrai. En d'autres mots, + exactement une assertion parmi `a.eq(b)`, `a.lt(b)` et `a.gt(b)` est vraie. + + - *Cohérence avec `PartialOrd`*: `Some(a.cmp(b)) == a.partial_cmp(b)`. + +Le compilateur ne vérifie aucun de ces prérequis, à l'exception des +vérifications sur les types. Toutefois, les comparaisons sont des éléments +importants puisqu'elles jouent un rôle tant dans les propriétés de vivacité +des systèmes critiques comme des ordonnanceurs ou des répartiteurs de charge +que dans les algorithmes optimisés qui peuvent éventuellement utiliser des +blocs `unsafe`. Dans le premier cas d'usage, une mauvaise relation d'ordre +peut causer des problèmes de disponibilité comme des interblocages. Dans le +second cas, cela peut mener à des problèmes classiques de sécurité liés à des +violations de propriétés de sûreté mémoire. C'est là encore un atout que de +limiter au possible l'utilisation des blocs `unsafe`. + +> **Règle {{#check LANG-CMP-INV | Respect des invariants des traits de comparaison standards}}** +> +> Dans un développement sécurisé en Rust, l'implémentation des traits de +> comparaison standards doit respecter les invariants décrits dans la +> documentation. + +> **Recommandation {{#check LANG-CMP-DEFAULTS | Utilisation des implémentations par défaut des traits de comparaison standards}}** +> +> Dans un développement sécurisé en Rust, l'implémentation des traits de +> comparaison standard ne doit être effectuée que par l'implémentation des +> méthodes ne fournissant pas d'implémentation par défaut, dans le but de +> réduire le risque de violation des invariants associés auxdits traits. + +Il existe un *lint* Clippy qui permet de vérifier que `PartialEq::ne` n'est pas +défini lors d'une implémentation du trait `PartialEq`. + +Rust propose une façon de fournir automatiquement des implémentations par défaut +pour les traits de comparaison, au travers de l'attribut `#[derive(...)]` : + +- La dérivation de `PartialEq` implémente `PartialEq` avec une **égalité + structurelle** à condition que chacun des types des données membres implémente + `PartialEq`. +- La dérivation de `Eq` implémente le trait de marquage `Eq` à condition que + chacun des types des données membres implémente `Eq`. +- La dérivation de `PartialOrd` implémente `PartialOrd` comme un **ordre + lexicographique** à condition que chacun des types des données membres + implémente `PartialOrd`. +- La dérivation de `Ord` implémente `Ord` comme un **ordre lexicographique** à + condition que chacun des types des données membres implémente `Ord`. + +Par exemple, le court extrait de code suivant montre comment comparer deux +valeurs de type `T1` facilement. Toutes les assertions sont vraies. + +```rust +#[derive(PartialEq, Eq, PartialOrd, Ord)] +struct T1 { + a: u8, b: u8 +} + +# fn main() { +assert!(&T1 { a: 0, b: 0 } == Box::new(T1 { a: 0, b: 0 }).as_ref()); +assert!(T1 { a: 1, b: 0 } > T1 { a: 0, b: 0 }); +assert!(T1 { a: 1, b: 1 } > T1 { a: 1, b: 0 }); +# println!("tous les tests sont validés."); +# } +``` + +> **Attention** +> +> La dérivation des traits de comparaison pour les types composites dépend de +> **l'ordre de déclaration des champs** et non de leur nom. +> +> D'abord, cela implique que changer l'ordre des champs modifie l'ordre des +> valeurs. Par exemple, en considérant le type suivant : +> +> ```rust,noplaypen +> #[derive(PartialEq, Eq, PartialOrd, Ord)] +> struct T2{ +> b: u8, a: u8 +> }; +> ``` +> +> on a `T1 {a: 1, b: 0} > T1 {a: 0, b: 1}` mais +> `T2 {a: 1, b: 0} < T2 {a: 0, b: 1}`. +> +> Ensuite, si une comparaison sous-jacente provoque un `panic`, l'ordre peut +> changer le résultat à cause de l'utilisation d'un opérateur logique court- +> circuitant dans l'implémentation automatique. +> +> Pour les énumérations, les comparaisons dérivées dépendent d'abord de +> **l'ordre des variants**, puis de l'ordre des champs. + +En dépit de ces avertissements sur les ordres dérivés, les comparaisons dérivées +automatiquement sont bien moins sujettes à erreurs que des implémentations +manuelles, et rendent le code plus court et plus simple à maintenir. + +> **Recommandation {{#check LANG-CMP-DERIVE | Dérivation des traits de comparaison lorsque c'est possible}}** +> +> Dans un développement sécurisé en Rust, l'implémentation des traits de +> comparaison standard doit être automatiquement dérivée à l'aide de +> `#[derive(...)]` lorsque l'égalité structurelle et l'ordre lexicographique +> sont nécessaires. Toute implémentation manuelle d'un trait de comparaison +> standard doit être justifiée et documentée. \ No newline at end of file diff --git a/src/fr/05_central_traits.md b/src/fr/05_central_traits.md new file mode 100644 index 0000000..53f4f5d --- /dev/null +++ b/src/fr/05_central_traits.md @@ -0,0 +1,66 @@ +# Traits centraux + +Implémenter ces traits modifie la sémantique d'exécution du langage. + +## Trait `Drop` : le destructeur + +Les types implémentent le trait `std::ops::Drop` dans le but d'effectuer +certaines opérations lorsque la mémoire associée à une valeur est réclamée. +`Drop` est l'équivalent Rust d'un destructeur en C++ ou un finaliseur en Java. + +`Drop` agit récursivement, depuis la valeur externe vers les valeurs imbriquées. +Lorsqu'une valeur sort du scope (ou est explicitement relâchée avec +`std::mem::drop`), elle est relâchée en deux étapes. La première étape a lieu +uniquement si le type de la valeur en question implémente le trait `Drop` et +consiste en l'appel de la méthode `drop`. La seconde étape consiste en la +répétition de processus de *drop* récursivement sur tous les champs que contient +la valeur. Il est à noter que l'implémentation de `Drop` est +*responsable uniquement de la valeur extérieure*. + +Tout d'abord, l'implémentation de `Drop` ne doit pas être systématique. Elle est +nécessaire uniquement lorsque le type requiert un traitement logique à la +destruction. `Drop` est typiquement utilisé dans le cas du relâchement des +ressources externes (connexions réseau, fichier, etc.) ou de ressources mémoire +complexes (*smart pointers* comme les `Box` ou les `Rc` par exemple). Au +final, il est probable que l'implémentation du trait `Drop` contienne des blocs +`unsafe` ainsi que d'autres opérations critiques du point de vue de la sécurité. + +> **Recommandation {{#check LANG-DROP | Justification de l'implémentation du trait `Drop`}}** +> +> Dans un développement sécurisé en Rust, l'implémentation du trait +> `std::ops::Drop` doit être justifiée, documentée et examinée par des pairs. + +Ensuite, le système de types de Rust assure seulement la sûreté mémoire et, +du point de vue du typage, des `drop`s peuvent tout à fait être manqués. +Plusieurs situations peuvent mener à manquer des `drop`s, comme : + +- un cycle dans la référence (par exemple avec `Rc` ou `Arc`) ; +- un appel explicite à `std::mem::forget` (ou `core::mem::forget`) (voir + paragraphe à propos de [`forget` et des fuites de mémoire](05_memory.html#forget-et-fuites-de-mémoire) ; +- un `panic` dans un `drop` ; +- un arrêt du programme (et un `panic` lorsque `abort-on-panic` est activé). + +Les `drop`s manqués peuvent mener à l'exposition de données sensibles ou bien +encore à l'épuisement de ressources limitées et par là même à des problèmes +d'indisponibilité. + +> **Règle {{#check LANG-DROP-NO-PANIC | Absence de `panic` dans l'implémentation de `Drop`}}** +> +> Dans un développement sécurisé en Rust, l'implémentation du trait +> `std::ops::Drop` ne doit pas causer de `panic`. + +En plus des `panic`s, les `drop`s contenant du code critique doivent être +protégés. + +> **Règle {{#check LANG-DROP-NO-CYCLE | Absence de cycles de références avec valeurs `Drop`ables}}** +> +> Les valeurs dont le type implémente `Drop` ne doivent pas être incluses, +> directement ou indirectement, dans un cycle de références à compteurs. + + + +> **Recommandation {{#check LANG-DROP-SEC | Sécurité assurée par d'autres mécanismes en plus du trait `Drop`}}** +> +> Certaines opérations liées à la sécurité d'une application à la fin d'un +> traitement (comme l'effacement de secrets cryptographiques par exemple) ne +> doivent pas reposer uniquement sur l'implémentation du trait `Drop`. diff --git a/src/fr/05_errors.md b/src/fr/05_errors.md new file mode 100644 index 0000000..d2c3c74 --- /dev/null +++ b/src/fr/05_errors.md @@ -0,0 +1,101 @@ +# Gestion des erreurs + + + +Le type `Result` est la façon privilégiée en Rust pour décrire le type de retour +des fonctions dont le traitement peut échouer. Un objet `Result` doit être +testé et jamais ignoré. + +> **Recommandation {{#check LANG-ERRWRAP | Mise en place d'un type `Error` personnalisé, pouvant contenir toutes les erreurs possibles}}** +> +> Une *crate* peut implanter son propre type `Error` qui peut contenir toutes +> les erreurs possibles. Des précautions supplémentaires doivent être prises : +> ce type doit être *exception-safe* (RFC 1236) et implémenter les traits +> `Error + Send + Sync + 'static` ainsi que `Display`. + +> **Recommandation {{#check LANG-ERRDO | Utilisation de l'opérateur `?` et non-utilisation de la macro `try!`}}** +> +> L'opérateur `?` doit être utilisé pour améliorer la lisibilité du code. +> La macro `try!` ne doit pas être utilisée. + +Des *crates* tierces peuvent être utilisées pour faciliter la gestion d'erreurs. +La plupart ([failure], [snafu], [thiserror]) proposent la création de types +d'erreurs personnalisées qui implémentent les traits nécessaires et permettent +l'encapsulation d'autres erreurs. + +Une autre approche (notamment proposée dans [anyhow]) consiste à envelopper +automatiquement les erreurs dans un seul type d'erreur universel. Une telle +approche ne devrait pas être utilisée dans des bibliothèques ou des systèmes +complexes parce qu'elle ne permet pas de fournir de contexte sur les erreurs +ainsi initialement enveloppées, contrairement à la première approche. + +[failure]: https://crates.io/crates/failure +[snafu]: https://crates.io/crates/snafu +[thiserror]: https://crates.io/crates/thiserror +[anyhow]: https://crates.io/crates/anyhow + +## *Panics* + +La gestion explicite des erreurs (`Result`) doit être préférée à la place de +l'utilisation de la macro `panic`. La cause de l'erreur doit être rendue +disponible, et les erreurs trop génériques doivent être évitées. + +Les *crates* fournissant des bibliothèques ne doivent pas utiliser de fonctions +ou d'instructions qui peuvent échouer en engendrant un `panic`. + +Des motifs courants de code qui provoquent des `panic` sont : + +- une utilisation de `unwrap` ou de `expect` ; +- une utilisation de `assert` ; +- un accès non vérifié à un tableau ; +- un dépassement d'entier (en mode *debug*) ; +- une division par zéro ; +- l'utilisation de `format!` pour le formatage d'une chaîne de caractères. + +> **Règle {{#check LANG-NOPANIC | Non-utilisation de fonctions qui peuvent causer des `panic`}}** +> +> Les fonctions et instructions qui peuvent causer des `panic` à l'exécution +> ne doivent pas être utilisées. + +> **Règle {{#check LANG-ARRINDEXING | Test des indices d'accès aux tableaux ou utilisation de la méthode `get`}}** +> +> L'indice d'accès à un tableau doit être testé, ou la méthode `get` doit être +> utilisée pour récupérer une `Option`. + + + + +## FFI et `panic`s + +Lorsque du code Rust est appelé depuis du code écrit dans un autre +langage (par exemple, du code C), le code Rust doit être écrit de sorte à ne +jamais pouvoir paniquer. +Dérouler (*unwinding*) depuis le code Rust vers le code étranger résulte en un +comportement indéfini. + +> **Règle {{#check LANG-FFIPANIC | Gestion correcte des `panic!` dans les FFI}}** +> +> Le code Rust appelé depuis une FFI doit soit être assuré de ne pas paniquer, +> soit utiliser `catch_unwind` ou le module `std::panic` pour s'assurer qu'il +> ne va pas abandonner un traitement puis que l'exécution retourne dans le +> langage appelant dans un état instable. + +Il est porté à l'attention du développeur que `catch_unwind` ne va traiter que +les cas de `panic`, et va préserver les abandons de processus causés par +d'autres raisons. + + + + diff --git a/src/fr/05_guarantees.md b/src/fr/05_guarantees.md new file mode 100644 index 0000000..ebbc1ee --- /dev/null +++ b/src/fr/05_guarantees.md @@ -0,0 +1,39 @@ +# Garanties du langage + + +## Comportements indéfinis + +> Le comportement d'un programme est *indéfini* (*UB* pour *Undefined Behavior*) lorsque sa sémantique n'est +> pas décrite dans le langage Rust. + +L'existence d'*UB* est considéré comme une [erreur](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.general). + +Par exemple le déréférencement d'un pointeur null est un *UB*. +*A contrario*, un `unwrap` sur l'objet `None` est bien *défini* car c'est le langage qui traite cette erreur +(en lançant un `panic`). + +La liste actuelle des *UB* est donnée [ici](https://doc.rust-lang.org/reference/behavior-considered-undefined.html). +On notera les garanties suivantes : + +* Pas de déréférencement de pointeur vers une adresse mémoire non allouée (*dangling pointer*) ou non alignée, ce qui implique + * Pas de dépassement de tableau + * Pas d'accès à de la mémoire libérée + * Accès toujours aligné quelque soit la plateforme +* Les valeurs pointées sont [cohérentes](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.invalid) avec le type du pointeur. Par exemple, une valeur pointée par un pointeur booléen sera l'octet 1 ou 0. +* Respect des règles d'[*aliasing*](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.alias) (voir aussi le [nomicon](https://doc.rust-lang.org/nomicon/aliasing.html)): une référence mutable ne peux être partagée. +* Pas d'accès concurrent (un accès en lecture et un autre en écriture ou en lecture) à la même adresse mémoire ([*data race*](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.race), voir aussi le [nomicon](https://doc.rust-lang.org/nomicon/races.html)) + +## Garantie de Rust + +> La volonté du langage est d'assurer l'absence d'*UB* dans un programme utilisant uniquement la partie non *unsafe* de Rust. + +Cependant, le langage ***ne protège pas*** contre les erreurs suivantes : + +* fuites de resources (mémoire, IO, ...) ; +* dépassements numériques. + +## Références + +* https://doc.rust-lang.org/reference/unsafety.html +* https://doc.rust-lang.org/nomicon/what-unsafe-does.html + diff --git a/src/fr/05_integer.md b/src/fr/05_integer.md new file mode 100644 index 0000000..c359f8b --- /dev/null +++ b/src/fr/05_integer.md @@ -0,0 +1,50 @@ +# Traitements des entiers + +## Dépassement d'entiers + +Bien que des vérifications soient effectuées par Rust en ce qui concerne les +potentiels dépassements d'entiers, des précautions doivent être prises lors de +l'exécution d'opérations arithmétiques sur les entiers. + +En particulier, il doit être noté que les profils de compilation *debug* et +*release* produisent des variations de comportements quant à la gestion des +dépassements d'entiers. Dans la configuration *debug*, un dépassement provoque +la terminaison du programme (`panic`), tandis que dans la configuration +*release* la valeur calculée est silencieusement tronquée en fonction de la +valeur maximum qui peut être stockée pour le type considéré. + +Ce comportement peut être rendu explicite en utilisant le type générique +`Wrapping`, ou les opérations sur les entiers `overflowing_` et +`wrapping_` (la partie `` étant remplacée par le type de calcul : +`add`, `mul`, `sub`, `shr`, etc.). + +```rust +use std::num::Wrapping; +# use std::panic; + +# fn main() { +let x: u8 = 242; + +# let result = panic::catch_unwind(|| { +println!("{}", x + 50); // panique en mode debug, affiche 36 en mode release. +# }); +# if result.is_err() { println!("panic"); } +println!("{}", x.overflowing_add(50).0); // affiche toujours 36. +println!("{}", x.wrapping_add(50)); // affiche toujours 36. +println!("{}", Wrapping(x) + Wrapping(50)); // affiche toujours 36. + +// panique toujours : +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"); } +# } +``` + +> **Règle {{#check LANG-ARITH | Utilisation des opérations arithmétiques appropriées au regard des potentiels dépassements}}** +> +> Lorsqu'une opération arithmétique peut produire un dépassement d'entier, les +> fonctions spécialisées `overflowing_`, `wrapping_` ou le type +> `Wrapping` doivent être utilisés. diff --git a/src/fr/05_macros.md b/src/fr/05_macros.md new file mode 100644 index 0000000..509cd31 --- /dev/null +++ b/src/fr/05_macros.md @@ -0,0 +1,8 @@ +# Macros + + + + \ No newline at end of file diff --git a/src/fr/05_naming.md b/src/fr/05_naming.md new file mode 100644 index 0000000..0296ad7 --- /dev/null +++ b/src/fr/05_naming.md @@ -0,0 +1,32 @@ +# Nommage + +La convention de nommage employée par la bibliothèque standard est *de facto* le +standard pour le nommage des éléments des programmes écrits en Rust. Un effort a +été fait pour formaliser ces conventions de nommage, d'abord dans la [RFC 430], +puis dans le document des *[Rust API Guidelines]*. + +La règle de base consiste à utiliser : + +- `UpperCamelCase` pour les types, traits et valeurs d'énumérations ; +- `snake_case` pour les fonctions, méthodes, macros, variables et modules ; +- `SCREAMING_SNAKE_CASE` pour les variables statiques et les constantes ; +- `'lowercase` pour les durées de vie (*lifetimes*). + +Les [Rust API Guidelines] recommandent également des conventions de nommage +plus précises pour certaines constructions particulières : + +- (C-CONV) pour les méthodes de conversion (`as_`, `to_`, `into_`) ; +- (C-GETTER) pour les accesseurs ; +- (C-ITER) pour les méthodes produisant des itérateurs ; +- (C-ITER-TY) pour les types itérateur ; +- (C-FEATURE) pour les noms de *features* ; +- (C-WORD-ORDER) pour la cohérence sur l'ordre des mots. + +> **Règle {{#check LANG-NAMING | Respect des conventions de nommage}}** +> +> Le développement d'une application sécurisée doit suivre les conventions de +> nommage décrites dans les [Rust API Guidelines]. + +[rfc 430]: https://github.com/rust-lang/rfcs/blob/master/text/0430-finalizing-naming-conventions.md +[rust api guidelines]: https://rust-lang.github.io/api-guidelines/ + diff --git a/src/fr/05_typesystem.md b/src/fr/05_typesystem.md index 58ec8ca..b4e932f 100644 --- a/src/fr/05_typesystem.md +++ b/src/fr/05_typesystem.md @@ -7,319 +7,3 @@ des confusions à propos du code qui est vraiment exécuté à la suite d'une résolution de contraintes de traits complexes). --> - -## Traits de la bibliothèque standard - -### Trait `Drop` : le destructeur - -Les types implémentent le trait `std::ops::Drop` dans le but d'effectuer -certaines opérations lorsque la mémoire associée à une valeur est réclamée. -`Drop` est l'équivalent Rust d'un destructeur en C++ ou un finaliseur en Java. - -`Drop` agit récursivement, depuis la valeur externe vers les valeurs imbriquées. -Lorsqu'une valeur sort du scope (ou est explicitement relâchée avec -`std::mem::drop`), elle est relâchée en deux étapes. La première étape a lieu -uniquement si le type de la valeur en question implémente le trait `Drop` et -consiste en l'appel de la méthode `drop`. La seconde étape consiste en la -répétition de processus de *drop* récursivement sur tous les champs que contient -la valeur. Il est à noter que l'implémentation de `Drop` est -*responsable uniquement de la valeur extérieure*. - -Tout d'abord, l'implémentation de `Drop` ne doit pas être systématique. Elle est -nécessaire uniquement lorsque le type requiert un traitement logique à la -destruction. `Drop` est typiquement utilisé dans le cas du relâchement des -ressources externes (connexions réseau, fichier, etc.) ou de ressources mémoire -complexes (*smart pointers* comme les `Box` ou les `Rc` par exemple). Au -final, il est probable que l'implémentation du trait `Drop` contienne des blocs -`unsafe` ainsi que d'autres opérations critiques du point de vue de la sécurité. - -> **Recommandation {{#check LANG-DROP | Justification de l'implémentation du trait `Drop`}}** -> -> Dans un développement sécurisé en Rust, l'implémentation du trait -> `std::ops::Drop` doit être justifiée, documentée et examinée par des pairs. - -Ensuite, le système de types de Rust assure seulement la sûreté mémoire et, -du point de vue du typage, des `drop`s peuvent tout à fait être manqués. -Plusieurs situations peuvent mener à manquer des `drop`s, comme : - -- un cycle dans la référence (par exemple avec `Rc` ou `Arc`) ; -- un appel explicite à `std::mem::forget` (ou `core::mem::forget`) (voir - paragraphe à propos de [`forget` et des fuites de mémoire](05_memory.html#forget-et-fuites-de-mémoire) ; -- un `panic` dans un `drop` ; -- un arrêt du programme (et un `panic` lorsque `abort-on-panic` est activé). - -Les `drop`s manqués peuvent mener à l'exposition de données sensibles ou bien -encore à l'épuisement de ressources limitées et par là même à des problèmes -d'indisponibilité. - -> **Règle {{#check LANG-DROP-NO-PANIC | Absence de `panic` dans l'implémentation de `Drop`}}** -> -> Dans un développement sécurisé en Rust, l'implémentation du trait -> `std::ops::Drop` ne doit pas causer de `panic`. - -En plus des `panic`s, les `drop`s contenant du code critique doivent être -protégés. - -> **Règle {{#check LANG-DROP-NO-CYCLE | Absence de cycles de références avec valeurs `Drop`ables}}** -> -> Les valeurs dont le type implémente `Drop` ne doivent pas être incluses, -> directement ou indirectement, dans un cycle de références à compteurs. - - - -> **Recommandation {{#check LANG-DROP-SEC | Sécurité assurée par d'autres mécanismes en plus du trait `Drop`}}** -> -> Certaines opérations liées à la sécurité d'une application à la fin d'un -> traitement (comme l'effacement de secrets cryptographiques par exemple) ne -> doivent pas reposer uniquement sur l'implémentation du trait `Drop`. - -### Les traits `Send` et `Sync` - -Les traits `Send` et `Sync` (définis dans `std::marker` ou `core::marker`) sont -des marqueurs utilisés pour assurer la sûreté des accès concurrents en Rust. -Lorsqu'ils sont correctement implémentés, ils permettent au compilateur Rust de -garantir l'absence de problèmes d'accès concurrents. Leurs sémantiques sont -définies comme suit : - -- Un type est `Send` s’il est sûr d'envoyer (*move*) des valeurs de ce type vers - un autre fil d'exécution. -- Un type est `Sync` s’il est sûr de partager des valeurs de ce type par une - référence immutable avec un autre fil d'exécution. - -Ces deux traits sont des traits *unsafe*, c'est-à-dire que le compilateur Rust -ne vérifie d'aucune manière que leur implémentation est correcte. Le danger est -réel : une implémentation incorrecte peut mener à un **comportement indéfini**. - -Heureusement, dans la plupart des cas, il n'est pas nécessaire de fournir une -implémentation. En Rust, la quasi-totalité des types primitifs implémente -`Send` et `Sync`, et dans la majorité des cas, Rust fournit de manière -automatique une implémentation pour les types composés. Quelques exceptions -notables sont : - -- les pointeurs `raw`, qui n'implémentent ni `Send`, ni `Sync`, puisqu'ils - n'offrent aucune garantie quant à la sûreté ; -- les références `UnsafeCell`, qui n'implémentent pas `Sync` (et par extensions, - les références `Cell` et `RefCell` non plus), puisqu'elles autorisent la - mutabilité des valeurs contenues (*interior mutability*) ; -- les références `Rc`, qui n'implémentent ni `Send`, ni `Sync`, puisque les - compteurs de références seraient partagés de manière désynchronisée. - -L'implémentation automatique de `Send` (respectivement `Sync`) a lieu pour les -types composés (structures ou énumérations) lorsque tous les champs contenus -implémentent `Send` (respectivement `Sync`). Une fonctionnalité notable, mais -**instable**, de Rust (depuis 1.37.0) permet d'empêcher cette implémentation -automatique en annotant explicitement le type considéré avec une -_négation d'implementation_ : - -```rust,ignore,noplaypen -#![feature(option_builtin_traits)] - -struct SpecialType(u8); -impl !Send for SpecialType {} -impl !Sync for SpecialType {} -``` -L'implémentation négative de `Send` ou `Sync` est également utilisée dans la -bibliothèque standard pour les exceptions, et est automatiquement implémentée -lorsque cela est approprié. En résultat, la documentation générée est toujours -explicite : un type implémente soit `Send` (respectivement `Sync`), soit -`!Send` (respectivement `!Sync`). - -En guise d'alternative *stable* à l'implémentation négative, il est possible -d'utiliser un champ typé par un type fantôme (`PhantomData`) : - -```rust,noplaypen -# use std::marker::PhantomData; -# -struct SpecialType(u8, PhantomData<*const ()>); -``` - -> **Recommandation {{#check LANG-SYNC-TRAITS | Justification de l'implémentation des traits `Send` et `Sync`}}** -> -> Dans un développement sécurisé en Rust, l'implémentation manuelle des traits -> `Send` et `Sync` doit être évitée, et, si nécessaire, doit être justifiée, -> documentée et révisée par des pairs. - -### Les traits de comparaison : `PartialEq`, `Eq`, `PartialOrd`, `Ord` - -Les comparaisons (`==`, `!=`, `<`, `<=`, `>`, `>=`) en Rust reposent sur quatre -traits de la bibliothèque standard disponibles dans `std::cmp` (ou `core::cmp` -pour une compilation avec `no_std`) : - -- `PartialEq` qui définit la relation d'équivalence partielle entre objets - de types `Self` et `Rhs` ; -- `PartialOrd` qui définit la relation d'ordre partiel entre les objets de - types `Self` et `Rhs` ; -- `Eq` qui définit la relation d'équivalence totale entre les objets du même - type. Il s'agit d'un trait de marquage qui requiert le trait - `PartialEq` ; -- `Ord` qui définit la relation d'ordre total entre les objets du même type. - Le trait `PartialOrd` est alors requis. - -Comme stipulé dans la documentation de la bibliothèque standard, Rust présuppose -**de nombreux invariants** lors de l'implémentation de ces traits : - -- Pour `PartialEq` : - - - *Cohérence interne* : `a.ne(b)` est équivalent à `!a.eq(b)`, c.-à-d., `ne` - est le strict inverse de `eq`. Cela correspond précisément à - l'implémentation par défaut de `ne`. - - - *Symétrie* : `a.eq(b)` et `b.eq(a)` sont équivalents. Du point de vue du - développeur, cela signifie que : - - - `PartialEq` est implémenté pour le type `A` (noté `A: PartialEq`). - - `PartialEq` est implémenté pour le type `B` (noté `B: PartialEq`). - - Les deux implémentations sont cohérentes l'une avec l'autre. - - - *Transitivité* : `a.eq(b)` et `b.eq(c)` impliquent `a.eq(c)`. Cela signifie - que : - - - `A: PartialEq`. - - `B: PartialEq`. - - `A: PartialEq`. - - Les trois implémentations sont cohérentes les unes avec les autres (ainsi - qu'avec leurs implémentations symétriques). - -- Pour `Eq` : - - - `PartialEq` est implémenté. - - - *Réflexivité* : `a.eq(a)`. Cela signifie que `PartialEq` est - implémenté (`Eq` ne fournit aucune méthode). - -- Pour `PartialOrd` : - - - *Consistance de la relation d'égalité* : `a.eq(b)` est équivalent à - `a.partial_cmp(b) == Some(std::ordering::Eq)`. - - - *Consistence interne* : - - - `a.lt(b)` ssi `a.partial_cmp(b) == Some(std::ordering::Less)`. - - `a.gt(b)` ssi `a.partial_cmp(b) == Some(std::ordering::Greater)`. - - `a.le(b)` ssi `a.lt(b) || a.eq(b)`. - - `a.ge(b)` ssi `a.gt(b) || a.eq(b)`. - - Il faut noter qu'en définissant seulement `partial_cmp`, la consistance - interne est garantie par les implémentations par défaut de `lt`, `le`, `gt`, - and `ge`. - - - *Antisymétrie* : `a.lt(b)` (respectivement `a.gt(b)`) implique `b.gt(a)` - (respectivement `b.lt(b)`). Du point de vue du développeur, cela signifie - que : - - - `A: PartialOrd`. - - `B: PartialOrd`. - - Les deux implémentations sont cohérentes l'une avec l'autre. - - - *Transitivité* : `a.lt(b)` et `b.lt(c)` impliquent `a.lt(c)` (également avec - `gt`, `le` et `ge`). Cela signifie que : - - - `A: PartialOrd`. - - `B: PartialOrd`. - - `A: PartialOrd`. - - Les trois implémentations sont cohérentes les unes avec les autres (et - avec leurs implémentations symétriques). - -- Pour `Ord` : - - - `PartialOrd` - - - *Totalité* : `a.partial_cmp(b) != None` est toujours vrai. En d'autres mots, - exactement une assertion parmi `a.eq(b)`, `a.lt(b)` et `a.gt(b)` est vraie. - - - *Cohérence avec `PartialOrd`*: `Some(a.cmp(b)) == a.partial_cmp(b)`. - -Le compilateur ne vérifie aucun de ces prérequis, à l'exception des -vérifications sur les types. Toutefois, les comparaisons sont des éléments -importants puisqu'elles jouent un rôle tant dans les propriétés de vivacité -des systèmes critiques comme des ordonnanceurs ou des répartiteurs de charge -que dans les algorithmes optimisés qui peuvent éventuellement utiliser des -blocs `unsafe`. Dans le premier cas d'usage, une mauvaise relation d'ordre -peut causer des problèmes de disponibilité comme des interblocages. Dans le -second cas, cela peut mener à des problèmes classiques de sécurité liés à des -violations de propriétés de sûreté mémoire. C'est là encore un atout que de -limiter au possible l'utilisation des blocs `unsafe`. - -> **Règle {{#check LANG-CMP-INV | Respect des invariants des traits de comparaison standards}}** -> -> Dans un développement sécurisé en Rust, l'implémentation des traits de -> comparaison standards doit respecter les invariants décrits dans la -> documentation. - -> **Recommandation {{#check LANG-CMP-DEFAULTS | Utilisation des implémentations par défaut des traits de comparaison standards}}** -> -> Dans un développement sécurisé en Rust, l'implémentation des traits de -> comparaison standard ne doit être effectuée que par l'implémentation des -> méthodes ne fournissant pas d'implémentation par défaut, dans le but de -> réduire le risque de violation des invariants associés auxdits traits. - -Il existe un *lint* Clippy qui permet de vérifier que `PartialEq::ne` n'est pas -défini lors d'une implémentation du trait `PartialEq`. - -Rust propose une façon de fournir automatiquement des implémentations par défaut -pour les traits de comparaison, au travers de l'attribut `#[derive(...)]` : - -- La dérivation de `PartialEq` implémente `PartialEq` avec une **égalité - structurelle** à condition que chacun des types des données membres implémente - `PartialEq`. -- La dérivation de `Eq` implémente le trait de marquage `Eq` à condition que - chacun des types des données membres implémente `Eq`. -- La dérivation de `PartialOrd` implémente `PartialOrd` comme un **ordre - lexicographique** à condition que chacun des types des données membres - implémente `PartialOrd`. -- La dérivation de `Ord` implémente `Ord` comme un **ordre lexicographique** à - condition que chacun des types des données membres implémente `Ord`. - -Par exemple, le court extrait de code suivant montre comment comparer deux -valeurs de type `T1` facilement. Toutes les assertions sont vraies. - -```rust -#[derive(PartialEq, Eq, PartialOrd, Ord)] -struct T1 { - a: u8, b: u8 -} - -# fn main() { -assert!(&T1 { a: 0, b: 0 } == Box::new(T1 { a: 0, b: 0 }).as_ref()); -assert!(T1 { a: 1, b: 0 } > T1 { a: 0, b: 0 }); -assert!(T1 { a: 1, b: 1 } > T1 { a: 1, b: 0 }); -# println!("tous les tests sont validés."); -# } -``` - -> **Attention** -> -> La dérivation des traits de comparaison pour les types composites dépend de -> **l'ordre de déclaration des champs** et non de leur nom. -> -> D'abord, cela implique que changer l'ordre des champs modifie l'ordre des -> valeurs. Par exemple, en considérant le type suivant : -> -> ```rust,noplaypen -> #[derive(PartialEq, Eq, PartialOrd, Ord)] -> struct T2{ -> b: u8, a: u8 -> }; -> ``` -> -> on a `T1 {a: 1, b: 0} > T1 {a: 0, b: 1}` mais -> `T2 {a: 1, b: 0} < T2 {a: 0, b: 1}`. -> -> Ensuite, si une comparaison sous-jacente provoque un `panic`, l'ordre peut -> changer le résultat à cause de l'utilisation d'un opérateur logique court- -> circuitant dans l'implémentation automatique. -> -> Pour les énumérations, les comparaisons dérivées dépendent d'abord de -> **l'ordre des variants**, puis de l'ordre des champs. - -En dépit de ces avertissements sur les ordres dérivés, les comparaisons dérivées -automatiquement sont bien moins sujettes à erreurs que des implémentations -manuelles, et rendent le code plus court et plus simple à maintenir. - -> **Recommandation {{#check LANG-CMP-DERIVE | Dérivation des traits de comparaison lorsque c'est possible}}** -> -> Dans un développement sécurisé en Rust, l'implémentation des traits de -> comparaison standard doit être automatiquement dérivée à l'aide de -> `#[derive(...)]` lorsque l'égalité structurelle et l'ordre lexicographique -> sont nécessaires. Toute implémentation manuelle d'un trait de comparaison -> standard doit être justifiée et documentée. diff --git a/src/fr/SUMMARY.md b/src/fr/SUMMARY.md index 527617c..5eb577a 100644 --- a/src/fr/SUMMARY.md +++ b/src/fr/SUMMARY.md @@ -1,15 +1,30 @@ # Summary -- [Introduction](01_introduction.md) +[Introduction](01_introduction.md) + +# Cycle de vie + - [Environnement de développement](02_devenv.md) - [Bibliothèques](03_libraries.md) -- [Généralités sur le langage](04_language.md) -- [Système de types](05_typesystem.md) + +# Langage + +- [Garanties du langage](05_guarantees.md) +- [Nommage](05_naming.md) + +- [Gestion des entiers](05_integer.md) +- [Gestion des erreurs](05_errors.md) + +- [Traits centraux](05_central_traits.md) - [Unsafe Rust](06_unsafe.md) - [Généralités](06_1_unsafe_generalities.md) - [Gestion de la mémoire](06_2_unsafe_memory.md) - [FFI](06_3_unsafe_ffi.md) +# Écosystème + +- [Bibliothèque standard](04_standard.md) + [License](LICENSE.md) From 9daa41e8f2c7d9bff89634c040f48507e1d89142 Mon Sep 17 00:00:00 2001 From: hg-anssi Date: Tue, 22 Jul 2025 12:01:21 +0200 Subject: [PATCH 02/10] remove files numbering --- src/en/SUMMARY.md | 32 +++++++++---------- ...05_central_traits.md => central_traits.md} | 0 src/en/{02_devenv.md => devenv.md} | 0 src/en/{05_errors.md => errors.md} | 0 src/en/{05_guarantees.md => guarantees.md} | 0 src/en/{05_integer.md => integer.md} | 0 .../{01_introduction.md => introduction.md} | 0 src/en/{03_libraries.md => libraries.md} | 0 src/en/{05_macros.md => macros.md} | 0 src/en/{05_naming.md => naming.md} | 0 src/en/{04_standard.md => standard.md} | 0 src/en/{08_testfuzz.md => testfuzz.md} | 0 src/en/{05_typesystem.md => typesystem.md} | 0 src/en/{06_unsafe.md => unsafe.md} | 0 src/en/{06_3_unsafe_ffi.md => unsafe_ffi.md} | 0 ...generalities.md => unsafe_generalities.md} | 0 ...06_2_unsafe_memory.md => unsafe_memory.md} | 0 src/fr/SUMMARY.md | 32 +++++++++---------- ...05_central_traits.md => central_traits.md} | 0 src/fr/{02_devenv.md => devenv.md} | 0 src/fr/{05_errors.md => errors.md} | 0 src/fr/{05_guarantees.md => guarantees.md} | 0 src/fr/{05_integer.md => integer.md} | 0 .../{01_introduction.md => introduction.md} | 0 src/fr/{03_libraries.md => libraries.md} | 0 src/fr/{05_macros.md => macros.md} | 0 src/fr/{05_naming.md => naming.md} | 0 src/fr/{04_standard.md => standard.md} | 0 src/fr/{08_testfuzz.md => testfuzz.md} | 0 src/fr/{05_typesystem.md => typesystem.md} | 0 src/fr/{06_unsafe.md => unsafe.md} | 0 src/fr/{06_3_unsafe_ffi.md => unsafe_ffi.md} | 0 ...generalities.md => unsafe_generalities.md} | 0 ...06_2_unsafe_memory.md => unsafe_memory.md} | 0 34 files changed, 32 insertions(+), 32 deletions(-) rename src/en/{05_central_traits.md => central_traits.md} (100%) rename src/en/{02_devenv.md => devenv.md} (100%) rename src/en/{05_errors.md => errors.md} (100%) rename src/en/{05_guarantees.md => guarantees.md} (100%) rename src/en/{05_integer.md => integer.md} (100%) rename src/en/{01_introduction.md => introduction.md} (100%) rename src/en/{03_libraries.md => libraries.md} (100%) rename src/en/{05_macros.md => macros.md} (100%) rename src/en/{05_naming.md => naming.md} (100%) rename src/en/{04_standard.md => standard.md} (100%) rename src/en/{08_testfuzz.md => testfuzz.md} (100%) rename src/en/{05_typesystem.md => typesystem.md} (100%) rename src/en/{06_unsafe.md => unsafe.md} (100%) rename src/en/{06_3_unsafe_ffi.md => unsafe_ffi.md} (100%) rename src/en/{06_1_unsafe_generalities.md => unsafe_generalities.md} (100%) rename src/en/{06_2_unsafe_memory.md => unsafe_memory.md} (100%) rename src/fr/{05_central_traits.md => central_traits.md} (100%) rename src/fr/{02_devenv.md => devenv.md} (100%) rename src/fr/{05_errors.md => errors.md} (100%) rename src/fr/{05_guarantees.md => guarantees.md} (100%) rename src/fr/{05_integer.md => integer.md} (100%) rename src/fr/{01_introduction.md => introduction.md} (100%) rename src/fr/{03_libraries.md => libraries.md} (100%) rename src/fr/{05_macros.md => macros.md} (100%) rename src/fr/{05_naming.md => naming.md} (100%) rename src/fr/{04_standard.md => standard.md} (100%) rename src/fr/{08_testfuzz.md => testfuzz.md} (100%) rename src/fr/{05_typesystem.md => typesystem.md} (100%) rename src/fr/{06_unsafe.md => unsafe.md} (100%) rename src/fr/{06_3_unsafe_ffi.md => unsafe_ffi.md} (100%) rename src/fr/{06_1_unsafe_generalities.md => unsafe_generalities.md} (100%) rename src/fr/{06_2_unsafe_memory.md => unsafe_memory.md} (100%) diff --git a/src/en/SUMMARY.md b/src/en/SUMMARY.md index 7d67f57..05f2a8a 100644 --- a/src/en/SUMMARY.md +++ b/src/en/SUMMARY.md @@ -1,30 +1,30 @@ # Summary -[Introduction](01_introduction.md) +[Introduction](introduction.md) # Lifecycle -- [Development environment](02_devenv.md) -- [Libraries](03_libraries.md) +- [Development environment](devenv.md) +- [Libraries](libraries.md) # Language -- [Language guarantees](05_guarantees.md) -- [Naming](05_naming.md) - -- [Integer operations](05_integer.md) -- [Error handling](05_errors.md) - -- [Central traits](05_central_traits.md) -- [Unsafe Rust](06_unsafe.md) - - [Generalities](06_1_unsafe_generalities.md) - - [Memory management](06_2_unsafe_memory.md) - - [Foreign Function Interface](06_3_unsafe_ffi.md) +- [Language guarantees](guarantees.md) +- [Naming](naming.md) + +- [Integer operations](integer.md) +- [Error handling](errors.md) + +- [Central traits](central_traits.md) +- [Unsafe Rust](unsafe.md) + - [Generalities](unsafe_generalities.md) + - [Memory management](unsafe_memory.md) + - [Foreign Function Interface](unsafe_ffi.md) # Ecosystem -- [Standard library](04_standard.md) +- [Standard library](standard.md) [Licence](LICENCE.md) - + diff --git a/src/en/05_central_traits.md b/src/en/central_traits.md similarity index 100% rename from src/en/05_central_traits.md rename to src/en/central_traits.md diff --git a/src/en/02_devenv.md b/src/en/devenv.md similarity index 100% rename from src/en/02_devenv.md rename to src/en/devenv.md diff --git a/src/en/05_errors.md b/src/en/errors.md similarity index 100% rename from src/en/05_errors.md rename to src/en/errors.md diff --git a/src/en/05_guarantees.md b/src/en/guarantees.md similarity index 100% rename from src/en/05_guarantees.md rename to src/en/guarantees.md diff --git a/src/en/05_integer.md b/src/en/integer.md similarity index 100% rename from src/en/05_integer.md rename to src/en/integer.md diff --git a/src/en/01_introduction.md b/src/en/introduction.md similarity index 100% rename from src/en/01_introduction.md rename to src/en/introduction.md diff --git a/src/en/03_libraries.md b/src/en/libraries.md similarity index 100% rename from src/en/03_libraries.md rename to src/en/libraries.md diff --git a/src/en/05_macros.md b/src/en/macros.md similarity index 100% rename from src/en/05_macros.md rename to src/en/macros.md diff --git a/src/en/05_naming.md b/src/en/naming.md similarity index 100% rename from src/en/05_naming.md rename to src/en/naming.md diff --git a/src/en/04_standard.md b/src/en/standard.md similarity index 100% rename from src/en/04_standard.md rename to src/en/standard.md diff --git a/src/en/08_testfuzz.md b/src/en/testfuzz.md similarity index 100% rename from src/en/08_testfuzz.md rename to src/en/testfuzz.md diff --git a/src/en/05_typesystem.md b/src/en/typesystem.md similarity index 100% rename from src/en/05_typesystem.md rename to src/en/typesystem.md diff --git a/src/en/06_unsafe.md b/src/en/unsafe.md similarity index 100% rename from src/en/06_unsafe.md rename to src/en/unsafe.md diff --git a/src/en/06_3_unsafe_ffi.md b/src/en/unsafe_ffi.md similarity index 100% rename from src/en/06_3_unsafe_ffi.md rename to src/en/unsafe_ffi.md diff --git a/src/en/06_1_unsafe_generalities.md b/src/en/unsafe_generalities.md similarity index 100% rename from src/en/06_1_unsafe_generalities.md rename to src/en/unsafe_generalities.md diff --git a/src/en/06_2_unsafe_memory.md b/src/en/unsafe_memory.md similarity index 100% rename from src/en/06_2_unsafe_memory.md rename to src/en/unsafe_memory.md diff --git a/src/fr/SUMMARY.md b/src/fr/SUMMARY.md index 5eb577a..e7bd4dc 100644 --- a/src/fr/SUMMARY.md +++ b/src/fr/SUMMARY.md @@ -1,30 +1,30 @@ # Summary -[Introduction](01_introduction.md) +[Introduction](introduction.md) # Cycle de vie -- [Environnement de développement](02_devenv.md) -- [Bibliothèques](03_libraries.md) +- [Environnement de développement](devenv.md) +- [Bibliothèques](libraries.md) # Langage -- [Garanties du langage](05_guarantees.md) -- [Nommage](05_naming.md) - -- [Gestion des entiers](05_integer.md) -- [Gestion des erreurs](05_errors.md) - -- [Traits centraux](05_central_traits.md) -- [Unsafe Rust](06_unsafe.md) - - [Généralités](06_1_unsafe_generalities.md) - - [Gestion de la mémoire](06_2_unsafe_memory.md) - - [FFI](06_3_unsafe_ffi.md) +- [Garanties du langage](guarantees.md) +- [Nommage](naming.md) + +- [Gestion des entiers](integer.md) +- [Gestion des erreurs](errors.md) + +- [Traits centraux](central_traits.md) +- [Unsafe Rust](unsafe.md) + - [Généralités](unsafe_generalities.md) + - [Gestion de la mémoire](unsafe_memory.md) + - [FFI](unsafe_ffi.md) # Écosystème -- [Bibliothèque standard](04_standard.md) +- [Bibliothèque standard](standard.md) [License](LICENSE.md) - + diff --git a/src/fr/05_central_traits.md b/src/fr/central_traits.md similarity index 100% rename from src/fr/05_central_traits.md rename to src/fr/central_traits.md diff --git a/src/fr/02_devenv.md b/src/fr/devenv.md similarity index 100% rename from src/fr/02_devenv.md rename to src/fr/devenv.md diff --git a/src/fr/05_errors.md b/src/fr/errors.md similarity index 100% rename from src/fr/05_errors.md rename to src/fr/errors.md diff --git a/src/fr/05_guarantees.md b/src/fr/guarantees.md similarity index 100% rename from src/fr/05_guarantees.md rename to src/fr/guarantees.md diff --git a/src/fr/05_integer.md b/src/fr/integer.md similarity index 100% rename from src/fr/05_integer.md rename to src/fr/integer.md diff --git a/src/fr/01_introduction.md b/src/fr/introduction.md similarity index 100% rename from src/fr/01_introduction.md rename to src/fr/introduction.md diff --git a/src/fr/03_libraries.md b/src/fr/libraries.md similarity index 100% rename from src/fr/03_libraries.md rename to src/fr/libraries.md diff --git a/src/fr/05_macros.md b/src/fr/macros.md similarity index 100% rename from src/fr/05_macros.md rename to src/fr/macros.md diff --git a/src/fr/05_naming.md b/src/fr/naming.md similarity index 100% rename from src/fr/05_naming.md rename to src/fr/naming.md diff --git a/src/fr/04_standard.md b/src/fr/standard.md similarity index 100% rename from src/fr/04_standard.md rename to src/fr/standard.md diff --git a/src/fr/08_testfuzz.md b/src/fr/testfuzz.md similarity index 100% rename from src/fr/08_testfuzz.md rename to src/fr/testfuzz.md diff --git a/src/fr/05_typesystem.md b/src/fr/typesystem.md similarity index 100% rename from src/fr/05_typesystem.md rename to src/fr/typesystem.md diff --git a/src/fr/06_unsafe.md b/src/fr/unsafe.md similarity index 100% rename from src/fr/06_unsafe.md rename to src/fr/unsafe.md diff --git a/src/fr/06_3_unsafe_ffi.md b/src/fr/unsafe_ffi.md similarity index 100% rename from src/fr/06_3_unsafe_ffi.md rename to src/fr/unsafe_ffi.md diff --git a/src/fr/06_1_unsafe_generalities.md b/src/fr/unsafe_generalities.md similarity index 100% rename from src/fr/06_1_unsafe_generalities.md rename to src/fr/unsafe_generalities.md diff --git a/src/fr/06_2_unsafe_memory.md b/src/fr/unsafe_memory.md similarity index 100% rename from src/fr/06_2_unsafe_memory.md rename to src/fr/unsafe_memory.md From db75126d05feb81ba29809072c42a3e263cfdb87 Mon Sep 17 00:00:00 2001 From: polazarus Date: Wed, 13 Aug 2025 08:27:37 +0200 Subject: [PATCH 03/10] chore: nitpick English edition --- src/en/devenv.md | 165 ++++++++++++++++++++--------------------- src/en/introduction.md | 94 +++++++++++------------ 2 files changed, 124 insertions(+), 135 deletions(-) diff --git a/src/en/devenv.md b/src/en/devenv.md index f81f028..b426680 100644 --- a/src/en/devenv.md +++ b/src/en/devenv.md @@ -1,17 +1,18 @@ -# Development environment +# Development Environment ## Rustup [Rustup] is the Rust toolchain installer. Among other things, it enables switching between different flavors of the toolchain (stable, beta, nightly), -managing additional components installation and keeping them up to date. +managing the installation of additional components, and keeping them up to date. > **Warning** > -> From a security perspective, `rustup` does perform all downloads over HTTPS, -> but does not yet validate signatures of downloads. Protection against -> downgrade attacks, certificate pinning, validation of signatures are still -> works in progress. +> From a security perspective, `rustup` performs all downloads over HTTPS, but +> does not yet validate signatures of downloads. Protection against downgrade +> attacks, certificate pinning, and validation of signatures are still works in +> progress. +> > In some cases, it may be preferable to opt for an alternative installation > method listed in the *Install* section of the official Rust website. @@ -21,29 +22,27 @@ managing additional components installation and keeping them up to date. Several flavors, called *editions*, of the Rust language coexist. -The concept of editions has been introduced to clarify new features -implementation and to make them incremental. A new edition will be produced -every two or three years, as stated in the [Edition Guide], but this doesn’t -mean that new features and improvements will only be shipped in a new edition. +The concept of editions was introduced to clarify the implementation of new +features and to make them incremental. A new edition is produced every two or +three years, as stated in the [Edition Guide], but this doesn’t mean that new +features and improvements are only shipped in a new edition. Some editions bring new keywords and language constructs. Recommendations for -secure applications development then remain closely linked to features of the -language, that are used in such applications, rather than to Rust editions. -In the rest of this guide, best effort will be made to highlight constructions -and language features that are specific to a particular Rust edition. +secure application development remain closely linked to the features of the +language used in such applications, rather than to Rust editions. In the rest of +this guide, best effort will be made to highlight constructions and language +features that are specific to a particular Rust edition. > **Note** > > No specific edition is recommended, as long as users follow the -> recommendations related to the features offered by the edition that has been -> chosen. +> recommendations related to the features offered by the chosen edition. [edition guide]: https://doc.rust-lang.org/edition-guide/ +### Stable, Nightly, and Beta Toolchains -### Stable, nightly and beta toolchains - -Orthogonally to editions that allow one to select a flavor (a set of features) +Orthogonally to editions, which allow one to select a flavor (a set of features) of the Rust language, the Rust toolchain is provided in three different versions, called *release channels*: @@ -51,7 +50,7 @@ versions, called *release channels*: - *beta* releases are created every six weeks, from promoted *nightly* releases, - *stable* releases are created every six weeks, from promoted *beta* releases. -When playing with different toolchains, it is important to check not only what +When working with different toolchains, it is important to check not only what the default toolchain is, but also if overrides are currently set for some directories. @@ -70,12 +69,12 @@ $ > **Rule {{#check DENV-STABLE | Use a stable compilation toolchain}}** > > Development of a secure application must be done using a fully stable -> toolchain, for limiting potential compiler, runtime or tool bugs. +> toolchain, to limit potential compiler, runtime, or tool bugs. -When using a specific `cargo` subcommand that requires a nightly component, -it is preferable to run it by switching the toolchain only locally, instead -of explicitly switching the complete toolchain. For example, to run the -(nightly) latest `rustfmt`: +When using a specific `cargo` subcommand that requires a nightly component, it +is preferable to run it by switching the toolchain only locally, instead of +explicitly switching the complete toolchain. For example, to run the (nightly) +latest `rustfmt`: ```shell $ rustup toolchain list @@ -91,62 +90,62 @@ $ ## Cargo Once Rustup has set up the appropriate Rust toolchain, [Cargo] is available -through the command line program `cargo`. Cargo is the Rust package manager. -It has a fundamental role in most Rust development: +through the command line program `cargo`. Cargo is the Rust package manager. It +plays a fundamental role in most Rust development: -- It structures project by providing the project skeleton (`cargo new`), +- It structures projects by providing the project skeleton (`cargo new`), - It compiles the project (`cargo build`), - It generates the project's documentation (`cargo doc`), - It runs tests (`cargo test`) and benchmarks (`cargo bench`), -- It manages and download dependencies, +- It manages and downloads dependencies, - It makes packages distributable and publishes them on [crates.io], -- It’s also a front-end to run complementary tools such as those that are - described below, in the form of sub-commands. +- It’s also a front-end to run complementary tools, such as those described + below, in the form of subcommands. > **Warning** > -> Like `rustup`, `cargo` does perform all downloads over HTTPS, but does not -> validate the registry index. Ongoing discussions occur on how to best protect -> and verify crates. For now, the security relies on the good security of the -> website [crates.io] and the GitHub hosted repository containing the +> Like `rustup`, `cargo` performs all downloads over HTTPS, but does not +> validate the registry index. Ongoing discussions are occurring on how to best +> protect and verify crates. For now, security relies on the good security of +> the website [crates.io] and the GitHub-hosted repository containing the > registry index. In some cases, it may be preferable to opt for an alternative > installation method for dependencies. -Cargo proposes many different commands and options to adapt the build process to +Cargo provides many different commands and options to adapt the build process to your project needs, mainly through the manifest file `Cargo.toml`. For a complete presentation, see [The Cargo Book]. -During the development of a secure application, some of the options may require -some attention. The `[profile.*]` sections allow configuring how the compiler is +During the development of a secure application, some options may require +attention. The `[profile.*]` sections allow configuring how the compiler is invoked. For example: -- the `debug-assertions` variable controls whether debug assertions are enabled, -- the `overflow-checks` variable controls whether overflows are checked for +- The `debug-assertions` variable controls whether debug assertions are enabled, +- The `overflow-checks` variable controls whether overflows are checked for integer arithmetic. -Overriding the default options may cause bugs not being detected, even when -using the debug profile that normally enables runtime checks (for example +Overriding the default options may cause bugs to go undetected, even when +using the debug profile that normally enables runtime checks (for example, [integer overflow checks](./04_language.html#integer-overflows)). > **Rule {{#check DENV-CARGO-OPTS | Keep default values for critical variables in cargo profiles}}** > > The variables `debug-assertions` and `overflow-checks` must not be overridden -> in development profiles sections (`[profile.dev]` and `[profile.test]`). - -Cargo proposes other ways to setup its configuration and change its behavior on -a given system. This can be very useful, but it may also be difficult to know -and remember at a given time all the options that are effectively used, and -in particular passed to the compiler. At the end, this can affect the confidence -and robustness of the build process. It is preferable to centralize compiler -options and flags in the configuration file `Cargo.toml`. For the case of -environment variable `RUSTC_WRAPPER`, for example, that may be used to generate -part of code or to run external tools before Rust compilation, it is preferable -to use the Cargo build scripts feature. +> in development profile sections (`[profile.dev]` and `[profile.test]`). + +Cargo offers other ways to set up its configuration and change its behavior on a +given system. This can be very useful, but it may also be difficult to know and +remember at a given time all the options that are effectively used, particularly +those passed to the compiler. Ultimately, this can affect the confidence and +robustness of the build process. It is preferable to centralize compiler options +and flags in the configuration file `Cargo.toml`. For example, the environment +variable `RUSTC_WRAPPER` may be used to generate part of the code or to run +external tools before Rust compilation; it is preferable to use the Cargo build +scripts feature. > **Rule {{#check DENV-CARGO-ENVVARS | Keep default values for compiler environment variables when running cargo}}** > -> The environment variables `RUSTC`, `RUSTC_WRAPPER` and `RUSTFLAGS` must not -> be overriden when using Cargo to build the project. +> The environment variables `RUSTC`, `RUSTC_WRAPPER`, and `RUSTFLAGS` must not +> be overridden when using Cargo to build the project. [crates.io]: https://crates.io [cargo]: https://doc.rust-lang.org/stable/cargo/ @@ -154,16 +153,16 @@ to use the Cargo build scripts feature. ### Clippy -[Clippy] is a tool that provides and checks many lints (bugs, styling, performance -issues, etc.). Since version 1.29, `clippy` can be used within the stable -`rustup` environment. It is recommended to install `clippy` as a component -(`rustup component add clippy`) in the stable toolchain instead of installing it -as a project dependency. +[Clippy] is a tool that provides and checks many lints (bugs, styling, +performance issues, etc.). Since version 1.29, `clippy` can be used within the +stable `rustup` environment. It is recommended to install `clippy` as a +component (`rustup component add clippy`) in the stable toolchain instead of +installing it as a project dependency. -The tool comes with some lint categories regarding the kind of issues it aims to -detect. The warnings should be re-checked by the programmer before committing -the fix that is suggested by `clippy`, especially in the case of lints of the -category `clippy::nursery` since those hints are still under development. +The tool comes with several lint categories regarding the kind of issues it aims +to detect. The warnings should be re-checked by the programmer before committing +the fix suggested by `clippy`, especially in the case of lints in the category +`clippy::nursery`, since those hints are still under development. > **Rule {{#check DENV-LINTER | Use linter regularly}}** > @@ -176,16 +175,16 @@ category `clippy::nursery` since those hints are still under development. [Rustfmt] is a tool that formats your code according to style guidelines. The documentation of the tool states some limitations, among others partial support -of macro declarations and uses. One should use the `--check` option that prints -proposed changes, review these changes, and finally apply them if the code -readability is not affected. +for macro declarations and uses. One should use the `--check` option, which +prints proposed changes, review these changes, and finally apply them if the +code readability is not affected. So, to launch it: ```shell -$ cargo fmt -- --check -$ # review of the changes -$ cargo fmt +cargo fmt -- --check +# review the changes +cargo fmt ``` These guidelines can be customized to your needs by creating a `rustfmt.toml` or @@ -205,46 +204,46 @@ at the [Rust Style Guide](https://doc.rust-lang.org/style-guide/index.html). > **Rule {{#check DENV-FORMAT | Use Rust formatter (rustfmt)}}** > > The tool `rustfmt` can be used to ensure that the codebase respects style -> guidelines (as described in `rustfmt.toml` file), with `--check` option and -> manual review. +> guidelines (as described in the `rustfmt.toml` file), with the `--check` +> option and manual review. [rustfmt]: https://github.com/rust-lang/rustfmt ### Rustfix -Included with Rust, since the end of 2018, [Rustfix] is a tool dedicated in +Included with Rust since the end of 2018, [Rustfix] is a tool dedicated to fixing compiler warnings as well as easing transitions between editions. ```shell -$ cargo fix +cargo fix ``` To prepare a Rust 2015 project to transition to Rust 2018, one can run: ```shell -$ cargo fix --edition +cargo fix --edition ``` Rustfix will either fix the code to be compatible with Rust 2018 or print a warning that explains the problem. This problem will have to be fixed manually. -By running the command (and possibly fixing manually some issues) until there -is no warning, one can ensure the code is compatible with both Rust 2015 and +By running the command (and possibly fixing some issues manually) until there +is no warning, one can ensure the code is compatible with both Rust 2015 and Rust 2018. To switch definitely to Rust 2018, one may run: ```shell -$ cargo fix --edition-idioms +cargo fix --edition-idioms ``` Be advised that this tool provides few guarantees on the soundness of the -proposed fixes. In particular mode, some corrections (such as some of those -provided with the `--edition-idioms`) are known to break the compilation -or change the program semantics in some case. +proposed fixes. In particular, some corrections (such as those provided with +the `--edition-idioms` option) are known to break compilation +or change program semantics in some cases. > **Rule {{#check DENV-AUTOFIX | Manually check automatic fixes}}** > -> In a secure Rust development, any automatic fix (for instance, provided by +> In secure Rust development, any automatic fix (for instance, provided by > `rustfix`) must be verified by the developer. [rustfix]: https://github.com/rust-lang-nursery/rustfix @@ -252,6 +251,6 @@ or change the program semantics in some case. ### Others There exist other useful tools or `cargo` subcommands for enforcing program -security whether by searching for specific code patterns or by providing +security, whether by searching for specific code patterns or by providing convenient commands for testing or fuzzing. They are discussed in the following chapters, according to their goals. diff --git a/src/en/introduction.md b/src/en/introduction.md index 69281fa..a7cc1a3 100644 --- a/src/en/introduction.md +++ b/src/en/introduction.md @@ -1,81 +1,71 @@ # Introduction -[Rust](https://www.rust-lang.org) is a multi-paradigm language with a focus on +[Rust](https://www.rust-lang.org) is a multi-paradigm language focused on memory safety. -It aims to be system programming oriented, for instance allowing fine-grained memory -management without garbage collection and without tedious and error-prone -manual memory allocations and deallocations, or preventing data-races. +It is designed for system programming, allowing fine-grained memory management +without garbage collection and without tedious or error-prone manual memory +allocations and deallocations. -It achieves this goal by means of -its ownership system (mostly related to variable aliasing). At any point of a -Rust program, the compiler tracks how many variables refer to a given data, and -enforces a set of rules which enable automatic memory management, memory safety -and data-race free programs. +Rust achieves this through its ownership system (primarily related to variable +aliasing). At any point in a Rust program, the compiler tracks how many +variables refer to a given piece of data, enforcing rules that enable automatic +memory management, memory safety, and data-race-free programs. -The language also focuses on performance, with powerful compilation -optimizations and language constructs that allow writing *zero-cost abstraction* +The language also emphasizes performance, with powerful compilation +optimizations and language constructs that allow writing _zero-cost abstraction_ code. -Moreover, the Rust language provides some high-level programming features. -Thanks to higher-order functions, closures, iterators, etc., it allows to write -program parts in the same vein as in functional programming languages. -Besides, static typing discipline, type inference, and ad hoc polymorphism (in -the form of traits) are other ways Rust provides to build libraries and programs -in a safe manner. +Additionally, Rust provides several high-level programming features. Thanks to +higher-order functions, closures, iterators, etc., it allows writing parts of +programs in a style similar to functional programming languages. Furthermore, +static typing, type inference, and ad hoc polymorphism (in the form of traits) +are other ways Rust enables building libraries and programs safely. -Finally, the toolchain (rustup, cargo) greatly facilitate the use of Rust by simplifying -the configuration of the software construction, while giving priority to good compilation -safety practices. - -Nevertheless, due to its versatility, the language possibly offers some -constructions that, if not used properly, can introduce security problems, +Nevertheless, due to its versatility, the language offers some +constructs that, if not used properly, can introduce security problems, by making code misinterpreted by the programmer or a reviewer. In addition, as -for every tool in the compilation or software verification field, the tools used - to develop, compile and execute programs can expose certain features or - configurations that, if misused, may lead to vulnerabilities. +with any tool in the compilation or software verification field, tools used +to develop, compile, and execute Rust programs can expose certain features or +configurations that, if misused, may lead to vulnerabilities. Thus, the objective of this document is to compile hints and recommendations to -stay in a safe zone for secure applications development while taking advantage -of the range of possibilities the Rust language can offer. +stay in a safe zone for secure application development while taking advantage +of the range of possibilities the Rust language offers. ## Target Audience -The guide intents to group recommendations that should be applied for -application development with strong security level requirements. Anyway, it can -be followed by everyone who wants to ensure that guarantees offered by the Rust -platform are not invalidated due to unsafe, misleading or unclear feature usage. +This guide aims to group recommendations that should be applied for +application development with strong security requirements. However, it can +be followed by anyone who wants to ensure that the guarantees offered by the Rust +platform are not invalidated due to unsafe, misleading, or unclear feature usage. -It is not intended to be a course on how to write Rust programs, there are +It is not intended to be a course on how to write Rust programs; there are already plenty of good learning resources for this purpose -(see for instance the +(see, for instance, the [Rust documentation main page](https://doc.rust-lang.org)). -The purpose is rather to guide the programmer and inform them about some pitfalls +The purpose is rather to guide programmers and inform them about some pitfalls they may encounter. -These recommendations form a complement to the good level of trust -the Rust language already provides. That said, recalls are sometimes necessary -for clarity, and the experienced Rust programmer may rely solely on -highlighted inserts (*Rule*, *Recommendation*, *Warning*, etc.). +These recommendations complement the high level of trust +the Rust language already provides. That said, reminders are sometimes necessary +for clarity, and experienced Rust programmers may rely solely on +highlighted inserts (_Rule_, _Recommendation_, _Warning_, etc.). ## Contributions This guide is written in a collaborative and open manner, via the GitHub -platform -([https://github.com/ANSSI-FR/rust-guide](https://github.com/ANSSI-FR/rust-guide)). -All contributions for future versions are welcome, whether in the form of direct -propositions (*pull requests*) or in the form of suggestions and discussions -(*issues*). +platform (). All contributions for +future versions are welcome, whether in the form of direct proposals (_pull +requests_) or in the form of suggestions and discussions (_issues_). ## Structure of the Document -This document considers separately different phases of a typical (and simplified) -development process. -Firstly, we provide some advices on how to take advantage of using tools of the -Rust ecosystem for secure development. -A following chapter focuses on precautions to take when choosing and using -external libraries. -Then, recommendations about the Rust language constructs are exposed. - A summary of recommendations presented throughout the document is listed at the end of this guide. From e3f290f5c7c0427826b6420adaaa7f10970ae112 Mon Sep 17 00:00:00 2001 From: polazarus Date: Wed, 13 Aug 2025 11:33:12 +0200 Subject: [PATCH 04/10] nitpick libraries --- src/en/libraries.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/en/libraries.md b/src/en/libraries.md index f31c00d..3b5244c 100644 --- a/src/en/libraries.md +++ b/src/en/libraries.md @@ -1,39 +1,39 @@ # Libraries In addition to the standard library, Rust provides an easy way to import other -libraries in a project, thanks to `cargo`. The libraries, known as *crates* in -the Rust ecosystem, are imported from the open-source components central -repository [crates.io](https://crates.io). +libraries into a project, thanks to `cargo`. The libraries, known as *crates* in +the Rust ecosystem, are imported from the open-source central repository +[crates.io](https://crates.io). -It should be noticed that the quality (in terms of security, performances, -readability, etc.) of the published crates is very variable. Moreover, their -maintenance can be irregular or interrupted. The usage of each component from +It should be noted that the quality (in terms of security, performance, +readability, etc.) of published crates is highly variable. Moreover, their +maintenance can be irregular or discontinued. The use of each component from this repository should be justified, and the developer should validate the -correct application of rules from the current guide in its code. Several tools -can aid in that task. +correct application of rules from this guide in its code. Several tools can +assist with that task. ## Cargo-outdated -[Cargo-outdated] tool allows one to easily manage dependencies versions. +The [cargo-outdated] tool allows one to easily manage dependency versions. -For a given crate, it lists current dependencies versions (using its -`Cargo.toml`), and checks latest compatible version and also latest general +For a given crate, it lists the current dependency versions (using its +`Cargo.toml`), checks the latest compatible version, and also the latest general version. -> **Rule {{#check LIBS-OUTDATED | Check for outdated dependencies versions (cargo-outdated)}}** +> **Rule {{#check LIBS-OUTDATED | Check for outdated dependencies (cargo-outdated)}}** > -> The `cargo-outdated` tool must be used to check dependencies status. Then, -> each outdated dependency must be updated or the choice of the version must be +> The `cargo-outdated` tool must be used to check the status of dependencies. +> Each outdated dependency must be updated, or the choice of version must be > justified. [cargo-outdated]: https://github.com/kbknapp/cargo-outdated ## Cargo-audit -[Cargo-audit] tool allows one to easily check for security vulnerabilities -reported to the RustSec Advisory Database. +The [cargo-audit] tool allows one to easily check for security vulnerabilities +reported in the RustSec Advisory Database. -> **Rule {{#check LIBS-AUDIT | Check for security vulnerabilities report on dependencies (cargo-audit)}}** +> **Rule {{#check LIBS-AUDIT | Check for security vulnerability reports on dependencies (cargo-audit)}}** > > The `cargo-audit` tool must be used to check for known vulnerabilities in > dependencies. @@ -51,6 +51,6 @@ dependencies. From 90b6ded11f11459f6c864183199ee1c84f876a1a Mon Sep 17 00:00:00 2001 From: polazarus Date: Wed, 13 Aug 2025 12:16:23 +0200 Subject: [PATCH 05/10] nitpick guarantees --- src/en/guarantees.md | 59 +++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/src/en/guarantees.md b/src/en/guarantees.md index 8a3d905..23e4b0e 100644 --- a/src/en/guarantees.md +++ b/src/en/guarantees.md @@ -1,33 +1,52 @@ -# Language guarantees +# Language Guarantees -## Undefined Behaviors (*UB*) +## Undefined Behavior (*UB*) -> The behavior of a program is *undefined* when its semantics is not described in the Rust language. +> The behavior of a program is *undefined* when its semantics is not described +> by the Rust language. -The existence of UB is considered an [error](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.general). +The existence of UB is considered an [error][reference ub]. -For example, dereferencing the null pointer is a *UB*. On the other hand, `unwrap`ing the `None` object is well defined because it is the language that processes this error (by launching a panic). +For example, dereferencing a null pointer is *UB*. On the other hand, +`unwrap`ping the `None` value is well defined because the language handles this +error (most often by triggering a panic). -The current list of *UBs* is given in the language [reference](https://doc.rust-lang.org/reference/behavior-considered-undefined.html). Notice the following guarantees: +The current list of *UBs* is given in the language [reference]. Note 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](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.invalid) 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](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.alias) (see also [nomicon](https://doc.rust-lang.org/nomicon/aliasing.html)): a mutable reference cannot be shared. -* No concurrent access (reading/writing is not possible while writing) to the same memory address ([data race](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.race), see also [nomicon](https://doc.rust-lang.org/nomicon/races.html)) +- No dereferencing of pointers to unallocated or unaligned memory addresses + (dangling pointers), which implies: + - No buffer overflows + - No access to freed memory + - No unaligned access +- [Consistency] between pointer types and pointed-to values (on read or write). For example, a + value pointed to by a Boolean pointer will be a byte with value 1 or 0. +- Respect for [aliasing rules][reference aliasing] + (see also [nomicon][nomicon aliasing]): a + mutable reference cannot be shared. +- No concurrent access (reading/writing is not possible while writing) to the + same memory address ([data race][reference data race], + see also [nomicon][nomicon data race]). -## Rust guarantees +## Rust Guarantees -> The language paradigm is to ensure the absence of a UB in a program using only the non-*unsafe* part of Rust. +> The language paradigm is to ensure the absence of UB in a program using only +> the non-*unsafe* parts of Rust. -However, the language does not prevent +However, **the language does not prevent**: -* resource leaks (memory, IO, ...), -* numeric overflows. +- resource leaks (memory, IO, etc.), +- numeric overflows. ## References -* https://doc.rust-lang.org/reference/unsafety.html -* https://doc.rust-lang.org/nomicon/what-unsafe-does.html +- +- + +[consistency]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.invalid +[nomicon aliasing]: https://doc.rust-lang.org/nomicon/aliasing.html +[nomicon data race]: https://doc.rust-lang.org/nomicon/races.html +[reference]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html +[reference aliasing]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.alias +[reference data race]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.race +[reference ub]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.general From f94bfe0b991ea98da27f60757c9a7756a16b7835 Mon Sep 17 00:00:00 2001 From: polazarus Date: Wed, 13 Aug 2025 12:16:34 +0200 Subject: [PATCH 06/10] nitpick guarantees fr --- src/fr/guarantees.md | 47 +++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/src/fr/guarantees.md b/src/fr/guarantees.md index ebbc1ee..baf3f56 100644 --- a/src/fr/guarantees.md +++ b/src/fr/guarantees.md @@ -1,39 +1,46 @@ # Garanties du langage +## Comportement indéfini (*UB*) -## Comportements indéfinis +> Le comportement d'un programme est *indéfini* (*UB* pour *Undefined Behavior*) lorsque sa sémantique n'est +> pas décrite par le langage Rust. -> Le comportement d'un programme est *indéfini* (*UB* pour *Undefined Behavior*) lorsque sa sémantique n'est -> pas décrite dans le langage Rust. +L'existence d'un *UB* est considérée comme une [erreur](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.general). -L'existence d'*UB* est considéré comme une [erreur](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.general). +Par exemple, le déréférencement d'un pointeur nul est un *UB*. *A contrario*, un +`unwrap` sur la valeur `None` est bien défini car c'est le langage qui traite +cette erreur (le plus souvent en lançant un `panic`). -Par exemple le déréférencement d'un pointeur null est un *UB*. -*A contrario*, un `unwrap` sur l'objet `None` est bien *défini* car c'est le langage qui traite cette erreur -(en lançant un `panic`). - -La liste actuelle des *UB* est donnée [ici](https://doc.rust-lang.org/reference/behavior-considered-undefined.html). +La liste actuelle des *UB* est donnée dans la [référence du langage](https://doc.rust-lang.org/reference/behavior-considered-undefined.html). On notera les garanties suivantes : -* Pas de déréférencement de pointeur vers une adresse mémoire non allouée (*dangling pointer*) ou non alignée, ce qui implique +* Pas de déréférencement de pointeur vers une adresse mémoire non allouée + (*dangling pointer*) ou non alignée, ce qui implique : * Pas de dépassement de tableau * Pas d'accès à de la mémoire libérée - * Accès toujours aligné quelque soit la plateforme -* Les valeurs pointées sont [cohérentes](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.invalid) avec le type du pointeur. Par exemple, une valeur pointée par un pointeur booléen sera l'octet 1 ou 0. -* Respect des règles d'[*aliasing*](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.alias) (voir aussi le [nomicon](https://doc.rust-lang.org/nomicon/aliasing.html)): une référence mutable ne peux être partagée. -* Pas d'accès concurrent (un accès en lecture et un autre en écriture ou en lecture) à la même adresse mémoire ([*data race*](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.race), voir aussi le [nomicon](https://doc.rust-lang.org/nomicon/races.html)) - -## Garantie de Rust + * Accès toujours aligné, quelle que soit la plateforme +* [Cohérence](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.invalid) + entre les valeurs pointées et le type du pointeur. Par exemple, une valeur + pointée par un pointeur booléen sera l'octet 1 ou 0. +* Respect des règles + d'[*aliasing*](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.alias) + (voir aussi le [nomicon](https://doc.rust-lang.org/nomicon/aliasing.html)) : + une référence mutable ne peut pas être partagée. +* Pas d'accès concurrent (lecture ou écriture en cas d'écriture) à la même adresse mémoire + ([*data race*](https://doc.rust-lang.org/reference/behavior-considered-undefined.html#r-undefined.race), + voir aussi le [nomicon](https://doc.rust-lang.org/nomicon/races.html)) + +## Garanties de Rust > La volonté du langage est d'assurer l'absence d'*UB* dans un programme utilisant uniquement la partie non *unsafe* de Rust. -Cependant, le langage ***ne protège pas*** contre les erreurs suivantes : +Cependant, le langage **ne protège pas** contre les erreurs suivantes : -* fuites de resources (mémoire, IO, ...) ; +* fuites de ressources (mémoire, IO, ...) ; * dépassements numériques. ## Références -* https://doc.rust-lang.org/reference/unsafety.html -* https://doc.rust-lang.org/nomicon/what-unsafe-does.html +* +* From a178d14705797372d6861a9aa502f58e0736f45e Mon Sep 17 00:00:00 2001 From: polazarus Date: Wed, 13 Aug 2025 14:26:54 +0200 Subject: [PATCH 07/10] nitpick naming --- src/en/naming.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/en/naming.md b/src/en/naming.md index 07d8dcc..ff8b2dc 100644 --- a/src/en/naming.md +++ b/src/en/naming.md @@ -1,18 +1,18 @@ # 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]. +Currently, the standard library serves as the de facto standard for naming +conventions in the Rust ecosystem. Efforts have been made to formalize these +conventions, first in [RFC 430], and later in the [Rust API Guidelines]. -The basic rule consists in using : +The basic rules are: -- `UpperCamelCase` for types, traits, enum variants, -- `snake_case` for functions, methods, macros, variables and modules, -- `SCREAMING_SNAKE_CASE` for statics and constants, +- `UpperCamelCase` for types, traits, and enum variants, +- `snake_case` for functions, methods, macros, variables, and modules, +- `SCREAMING_SNAKE_CASE` for statics, constants, and const generic parameters, - `'lowercase` for lifetimes. -The [Rust API Guidelines] also prescribes more precise naming conventions for -some particular constructions: +The [Rust API Guidelines] also prescribe more precise naming conventions for +specific constructs: - (C-CONV) for conversion methods (`as_`, `to_`, `into_`), - (C-GETTER) for getters, From fdbe02b69d85e483aac2accc6a3dc3172b38225f Mon Sep 17 00:00:00 2001 From: polazarus Date: Wed, 13 Aug 2025 14:27:17 +0200 Subject: [PATCH 08/10] nitpick naming fr --- src/fr/naming.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/fr/naming.md b/src/fr/naming.md index 0296ad7..0972f1c 100644 --- a/src/fr/naming.md +++ b/src/fr/naming.md @@ -7,9 +7,11 @@ puis dans le document des *[Rust API Guidelines]*. La règle de base consiste à utiliser : -- `UpperCamelCase` pour les types, traits et valeurs d'énumérations ; -- `snake_case` pour les fonctions, méthodes, macros, variables et modules ; -- `SCREAMING_SNAKE_CASE` pour les variables statiques et les constantes ; +- `UpperCamelCase` pour les types, traits et variants d'énumérations ; +- `snake_case` pour les fonctions, les méthodes, les macros, les variables et + les modules ; +- `SCREAMING_SNAKE_CASE` pour les statiques, les constantes et les paramètres + génériques constants ; - `'lowercase` pour les durées de vie (*lifetimes*). Les [Rust API Guidelines] recommandent également des conventions de nommage @@ -18,8 +20,9 @@ plus précises pour certaines constructions particulières : - (C-CONV) pour les méthodes de conversion (`as_`, `to_`, `into_`) ; - (C-GETTER) pour les accesseurs ; - (C-ITER) pour les méthodes produisant des itérateurs ; -- (C-ITER-TY) pour les types itérateur ; -- (C-FEATURE) pour les noms de *features* ; +- (C-ITER-TY) pour les types d'itérateurs ; +- (C-FEATURE) pour les noms de *features* (fonctionnalités accessibles par + configuration) ; - (C-WORD-ORDER) pour la cohérence sur l'ordre des mots. > **Règle {{#check LANG-NAMING | Respect des conventions de nommage}}** @@ -29,4 +32,3 @@ plus précises pour certaines constructions particulières : [rfc 430]: https://github.com/rust-lang/rfcs/blob/master/text/0430-finalizing-naming-conventions.md [rust api guidelines]: https://rust-lang.github.io/api-guidelines/ - From cd73236baea385dd36f65231a8f5649baf486b6b Mon Sep 17 00:00:00 2001 From: polazarus Date: Wed, 13 Aug 2025 15:40:33 +0200 Subject: [PATCH 09/10] nitpick integer --- src/en/integer.md | 56 +++++++++++++++++++++++++++++------------------ src/fr/integer.md | 53 ++++++++++++++++++++++++++++---------------- 2 files changed, 69 insertions(+), 40 deletions(-) diff --git a/src/en/integer.md b/src/en/integer.md index e8594ee..cf5be13 100644 --- a/src/en/integer.md +++ b/src/en/integer.md @@ -2,34 +2,48 @@ ## 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_` and `wrapping_` operations on integers -(the `` part being `add`, `mul`, `sub`, `shr`, etc.). +Although Rust performs some checks for potential integer overflows, precautions +should be taken when executing arithmetic operations on integers. + +In particular, note that the compilation profile (typically, *dev*, the default +debug build, or *release*, the standard optimized build) changes integer +overflow behavior. In the *dev* configuration, overflow causes the termination +of the program (`panic`), whereas in the *release* configuration, the computed +value is silently truncated to the number of bits of the numeric type, giving it +this wrap-around semantics. + +When an overflow is possible, the behavior can be made explicit by using +specific methods `_`, where `` can be `add`, `mul`, `sub`, `shr`, +etc.: + +- `checked_` returns `None` in case of overflow, +- `overflowing_` returns both a possibly wrapped result and a Boolean + indicating whether overflow occurred, +- `wrapping_` always returns the wrapped result, +- `saturating_` always returns the saturated result. + +For the last two choices, an alternative is to use the generic types `Wrapping` +and `Saturating` (from `std::num`) to accomplish the same thing in a more +concise way. Indeed, once the values are wrapped inside all subsequent +operations are made with the given semantics. ```rust -use std::num::Wrapping; +use std::num::{Saturating, 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. +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. +println!("{:?}", x.checked_add(50)); // always prints None. +println!("{}", x.overflowing_add(50).0); // always prints 36. +println!("{}", x.wrapping_add(50)); // always prints 36. +println!("{}", x.saturating_add(50)); // always prints 255. +println!("{}", Wrapping(x) + Wrapping(50)); // always prints 36. +println!("{}", Saturating(x) + Saturating(50)); // always prints 255. // always panics: let (res, c) = x.overflowing_add(50); @@ -43,6 +57,6 @@ else { println!("{}", res); } > **Rule {{#check LANG-ARITH | Use appropriate arithmetic operations regarding potential overflows}}** > -> When assuming that an arithmetic operation can produce an overflow, the -> specialized functions `overflowing_`, `wrapping_`, or the -> `Wrapping` type must be used. +> When an arithmetic operation may produce an overflow, specialized methods like +> `checked_`, `overflowing_`, `wrapping_`, or `saturating_`, or +> specialized wrapper types like `Wrapping` or `Saturating` must be used. diff --git a/src/fr/integer.md b/src/fr/integer.md index c359f8b..778f653 100644 --- a/src/fr/integer.md +++ b/src/fr/integer.md @@ -6,32 +6,46 @@ Bien que des vérifications soient effectuées par Rust en ce qui concerne les potentiels dépassements d'entiers, des précautions doivent être prises lors de l'exécution d'opérations arithmétiques sur les entiers. -En particulier, il doit être noté que les profils de compilation *debug* et -*release* produisent des variations de comportements quant à la gestion des -dépassements d'entiers. Dans la configuration *debug*, un dépassement provoque -la terminaison du programme (`panic`), tandis que dans la configuration -*release* la valeur calculée est silencieusement tronquée en fonction de la -valeur maximum qui peut être stockée pour le type considéré. - -Ce comportement peut être rendu explicite en utilisant le type générique -`Wrapping`, ou les opérations sur les entiers `overflowing_` et -`wrapping_` (la partie `` étant remplacée par le type de calcul : -`add`, `mul`, `sub`, `shr`, etc.). +En particulier, il doit être noté que les profils de compilation (typiquement, +*dev*, la compilation de debug par défaut, ou *release*, la compilation +optimisée standard) produisent des variations de comportements quant à la +gestion des dépassements d'entiers. Dans la configuration *dev*, un dépassement +provoque la terminaison du programme (`panic`), tandis que dans la configuration +*release*, la valeur calculée est silencieusement tronquée au nombre de bits du +type numérique, lui conférant cette sémantique d'arithmétique modulaire (*wrap +around*). + +Ce comportement peut être rendu explicite en utilisant des méthodes spécifiques +`_`, où `` peut être `add`, `mul`, `sub`, `shr`, etc. : + +- `checked_` retourne `None` en cas de dépassement, +- `overflowing_` retourne à la fois le résultat éventuellement tronqué et un + booléen indiquant si un dépassement a eu lieu, +- `wrapping_` retourne toujours le résultat tronqué, +- `saturating_` retourne toujours le résultat saturé. + +Pour les deux derniers choix, il est possible d'utiliser les types génériques +`Wrapping` et `Saturating` (de `std::num`) pour obtenir le même comportement de +manière plus concise. En effet, une fois les valeurs encapsulées, toutes les +opérations suivantes sont effectuées avec la sémantique choisie. ```rust -use std::num::Wrapping; +use std::num::{Saturating, Wrapping}; # use std::panic; # fn main() { let x: u8 = 242; # let result = panic::catch_unwind(|| { -println!("{}", x + 50); // panique en mode debug, affiche 36 en mode release. +println!("{}", x + 50); // panique en dev, affiche 36 en release. # }); # if result.is_err() { println!("panic"); } -println!("{}", x.overflowing_add(50).0); // affiche toujours 36. -println!("{}", x.wrapping_add(50)); // affiche toujours 36. -println!("{}", Wrapping(x) + Wrapping(50)); // affiche toujours 36. +println!("{:?}", x.checked_add(50)); // affiche toujours None. +println!("{}", x.overflowing_add(50).0); // affiche toujours 36. +println!("{}", x.wrapping_add(50)); // affiche toujours 36. +println!("{}", x.saturating_add(50)); // affiche toujours 255. +println!("{}", Wrapping(x) + Wrapping(50)); // affiche toujours 36. +println!("{}", Saturating(x) + Saturating(50)); // affiche toujours 255. // panique toujours : let (res, c) = x.overflowing_add(50); @@ -45,6 +59,7 @@ else { println!("{}", res); } > **Règle {{#check LANG-ARITH | Utilisation des opérations arithmétiques appropriées au regard des potentiels dépassements}}** > -> Lorsqu'une opération arithmétique peut produire un dépassement d'entier, les -> fonctions spécialisées `overflowing_`, `wrapping_` ou le type -> `Wrapping` doivent être utilisés. +> Lorsqu'une opération arithmétique peut produire un dépassement d'entier, il +> faut utiliser les méthodes spécialisées `checked_`, `overflowing_`, +> `wrapping_`, ou `saturating_`, ou les types génériques spécialisés +> `Wrapping` ou `Saturating`. From 705e3884ae6043f5966212d7420d8ac6012fc2e4 Mon Sep 17 00:00:00 2001 From: polazarus Date: Wed, 13 Aug 2025 15:59:45 +0200 Subject: [PATCH 10/10] nitpick licence fr --- src/fr/{LICENSE.md => LICENCE.md} | 0 src/fr/SUMMARY.md | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/fr/{LICENSE.md => LICENCE.md} (100%) diff --git a/src/fr/LICENSE.md b/src/fr/LICENCE.md similarity index 100% rename from src/fr/LICENSE.md rename to src/fr/LICENCE.md diff --git a/src/fr/SUMMARY.md b/src/fr/SUMMARY.md index e7bd4dc..47db310 100644 --- a/src/fr/SUMMARY.md +++ b/src/fr/SUMMARY.md @@ -25,6 +25,6 @@ - [Bibliothèque standard](standard.md) -[License](LICENSE.md) +[Licence](LICENCE.md)