|
| 1 | +.. SPDX-License-Identifier: MIT OR Apache-2.0 |
| 2 | + SPDX-FileCopyrightText: The Coding Guidelines Subcommittee Contributors |
| 3 | +
|
| 4 | +.. default-domain:: coding-guidelines |
| 5 | + |
| 6 | +Assure visibility of ``unsafe`` keyword in unsafe code |
| 7 | +====================================================== |
| 8 | + |
| 9 | +.. guideline:: Assure visibility of ``unsafe`` keyword in unsafe code |
| 10 | + :id: gui_ZDLZzjeOwLSU |
| 11 | + :category: required |
| 12 | + :status: draft |
| 13 | + :release: 1.85-latest |
| 14 | + :fls: fls_8kqo952gjhaf |
| 15 | + :decidability: decidable |
| 16 | + :scope: crate |
| 17 | + :tags: readability, reduce-human-error |
| 18 | + |
| 19 | + Mark all code that may violate safety guarantees with a visible ``unsafe`` keyword |
| 20 | + :cite:`gui_ZDLZzjeOwLSU:RUST-REF-UNSAFE-KEYWORD`. |
| 21 | + |
| 22 | + The following constructs require explicit ``unsafe`` visibility: |
| 23 | + |
| 24 | + * ``extern`` blocks must be declared as ``unsafe extern`` |
| 25 | + * The ``#[no_mangle]`` attribute must be written as ``#[unsafe(no_mangle)]`` |
| 26 | + * The ``#[export_name]`` attribute must be written as ``#[unsafe(export_name)]`` |
| 27 | + * The ``#[link_section]`` attribute must be written as ``#[unsafe(link_section)]`` |
| 28 | + |
| 29 | + .. note:: |
| 30 | + |
| 31 | + Starting with Rust Edition 2024, the use of ``unsafe`` is required in these contexts. |
| 32 | + The ``#[link]`` and ``#[link_ordinal]`` attributes are implicitly covered by the |
| 33 | + ``unsafe extern`` requirement, as they must appear on ``extern`` blocks. |
| 34 | + See rust-lang/rust#82499 for the tracking issue on unsafe attributes. |
| 35 | + |
| 36 | + .. rationale:: |
| 37 | + :id: rat_eQV3s9ggNegr |
| 38 | + :status: draft |
| 39 | + |
| 40 | + * Auditability and review |
| 41 | + |
| 42 | + * ``unsafe`` blocks create clear audit boundaries where reviewers can focus on code |
| 43 | + that may violate Rust's safety guarantees :cite:`gui_ZDLZzjeOwLSU:RUSTNOMICON-MEET-SAFE` |
| 44 | + * Safety-critical standards like ISO 26262 :cite:`gui_ZDLZzjeOwLSU:ISO-26262` and |
| 45 | + DO-178C :cite:`gui_ZDLZzjeOwLSU:DO-178C` require traceability of hazardous operations |
| 46 | + * Helps enumerate and document all places where safety requirements must be manually upheld |
| 47 | + * Satisfies ISO 26262 Part 6, Table 1, objective 1c (use of language subsets) |
| 48 | + * Satisfies DO-178C Section 6.3.4.f (source code traceability) |
| 49 | + |
| 50 | + * Explicit acknowledgment of responsibility |
| 51 | + |
| 52 | + * The ``unsafe`` keyword signals that the programmer is taking responsibility for |
| 53 | + upholding invariants the compiler cannot verify |
| 54 | + * Prevents accidental use of ``unsafe`` operations without conscious decision |
| 55 | + * Aligns with the principle of defense in depth in safety-critical systems |
| 56 | + |
| 57 | + * Static analysis and tooling |
| 58 | + |
| 59 | + * Tools like ``cargo-geiger`` :cite:`gui_ZDLZzjeOwLSU:CARGO-GEIGER`, ``unsafe-inspect``, |
| 60 | + and custom linters can automatically locate and count unsafe blocks |
| 61 | + * Enables metrics like "unsafe density" for safety assessments |
| 62 | + * Supports qualification evidence required by certification standards |
| 63 | + |
| 64 | + * Traceability for certification |
| 65 | + |
| 66 | + * Safety-critical certifications require demonstrating that hazardous operations are |
| 67 | + identified and controlled |
| 68 | + * Visible ``unsafe`` tokens provide direct linkage to safety cases and hazard analyses |
| 69 | + * Facilitates the required documentation that each unsafe operation has been reviewed |
| 70 | + and justified |
| 71 | + |
| 72 | + .. non_compliant_example:: |
| 73 | + :id: non_compl_ex_FdmuPXGZr4EP |
| 74 | + :status: draft |
| 75 | + |
| 76 | + The ``#[no_mangle]`` attribute is unsafe because it can be used to declare a function |
| 77 | + identifier that conflicts with an existing symbol. This noncompliant example declares an |
| 78 | + unmangled function named ``convert`` that is missing the required unsafe wrapper in Rust 2024. |
| 79 | + |
| 80 | + This noncompliant example requires Rust Edition 2021 or earlier to compile. |
| 81 | + In Rust Edition 2024, missing ``unsafe`` wrappers cause compilation errors. |
| 82 | + |
| 83 | + .. rust-example:: |
| 84 | + :compile_fail: |
| 85 | + :edition: 2024 |
| 86 | + |
| 87 | + // Undefined behavior by the linker or loader is possible |
| 88 | + // if another 'convert' function is defined. |
| 89 | + #[no_mangle] |
| 90 | + fn convert() {} |
| 91 | + |
| 92 | + fn main() { |
| 93 | + convert(); |
| 94 | + } |
| 95 | + |
| 96 | + .. compliant_example:: |
| 97 | + :id: compl_ex_wR1FEyLRKmrr |
| 98 | + :status: draft |
| 99 | + |
| 100 | + Rust Edition 2024 enforces that the ``no_mangle`` attribute requires an ``unsafe`` keyword, |
| 101 | + as shown in this compliant example. |
| 102 | + |
| 103 | + NOTE: This code can still have undefined behavior if the ``convert`` function symbol is |
| 104 | + defined more than once. |
| 105 | + |
| 106 | + .. rust-example:: |
| 107 | + :miri: skip |
| 108 | + :edition: 2024 |
| 109 | + |
| 110 | + #[unsafe(no_mangle)] // compliant |
| 111 | + fn convert() {} |
| 112 | + |
| 113 | + fn main() { |
| 114 | + convert(); |
| 115 | + } |
| 116 | + |
| 117 | + .. non_compliant_example:: |
| 118 | + :id: non_compl_ex_FdmuPXGZr4EO |
| 119 | + :status: draft |
| 120 | + |
| 121 | + This noncompliant example misdeclares the ``malloc`` function in an ``extern "C"`` block |
| 122 | + by specifying the type of the ``size`` parameter as ``f32`` instead of ``usize``. |
| 123 | + |
| 124 | + An ``extern`` block is unsafe because undefined behavior can occur if types or functions are |
| 125 | + misdeclared. This is true even if the declarations are not used. This noncompliant example |
| 126 | + requires Rust Edition 2021 or earlier to compile. |
| 127 | + |
| 128 | + .. rust-example:: |
| 129 | + :compile_fail: |
| 130 | + :edition: 2024 |
| 131 | + |
| 132 | + use std::ffi; |
| 133 | + |
| 134 | + extern "C" { |
| 135 | + // If 'malloc' is otherwise defined with a 'usize' argument, the compiler |
| 136 | + // may generate code for calls to this function using this incompatible declaration, |
| 137 | + // resulting in undefined behavior. |
| 138 | + fn malloc(size: f32) -> *mut ffi::c_void; |
| 139 | + } |
| 140 | +
|
| 141 | + fn main() {} |
| 142 | + |
| 143 | + .. compliant_example:: |
| 144 | + :id: compl_ex_wR1FEyLRKmrq |
| 145 | + :status: draft |
| 146 | + |
| 147 | + Rust Edition 2024 enforces that ``extern "C"`` blocks require an ``unsafe`` keyword, |
| 148 | + as shown in this compliant example. |
| 149 | + |
| 150 | + NOTE: This code can still have undefined behavior if the declared ``malloc`` function is |
| 151 | + incompatible with the actual definition. To eliminate this undefined behavior, the |
| 152 | + declaration for ``malloc`` used in this compliant example accepts one argument of type ``usize``. |
| 153 | + |
| 154 | + .. rust-example:: |
| 155 | + :miri: skip |
| 156 | + :edition: 2024 |
| 157 | + |
| 158 | + use std::ffi; |
| 159 | + |
| 160 | + unsafe extern "C" { |
| 161 | + // Here the assumption is that malloc is the one defined by C's stdlib.h |
| 162 | + // and that size_of::<usize>() == size_of::<size_t>() |
| 163 | + fn malloc(size: usize) -> *mut ffi::c_void; |
| 164 | + fn free(ptr: *mut ffi::c_void); |
| 165 | + } |
| 166 | +
|
| 167 | + fn main() { |
| 168 | + unsafe { |
| 169 | + let ptr = malloc(1024); |
| 170 | + if !ptr.is_null() { |
| 171 | + free(ptr); |
| 172 | + } |
| 173 | + } |
| 174 | + } |
| 175 | + |
| 176 | + .. non_compliant_example:: |
| 177 | + :id: non_compl_ex_Hk3mNp5qRs7t |
| 178 | + :status: draft |
| 179 | + |
| 180 | + The ``#[export_name]`` and ``#[link_section]`` attributes can cause undefined behavior if |
| 181 | + misused, as they affect symbol resolution and memory layout at link time. Without the |
| 182 | + ``unsafe`` keyword, these hazards are not visible to reviewers or tools. |
| 183 | + |
| 184 | + This noncompliant example has two separate problems. First, it uses an ``#[export_name]`` |
| 185 | + attribute without an unsafe wrapper. This attribute controls the symbol name used during |
| 186 | + linking. If another symbol with the same name exists, it causes undefined behavior. |
| 187 | + Rust 2024 requires it to be marked ``unsafe``. |
| 188 | + |
| 189 | + The second problem is that this noncompliant example uses a ``#[link_section]`` attribute |
| 190 | + without an unsafe wrapper. This attribute places the item in a specific linker section. |
| 191 | + Incorrect section placement can cause undefined behavior (e.g., placing mutable data in |
| 192 | + read-only sections, or interfering with special sections like ``.init``). |
| 193 | + |
| 194 | + This noncompliant example requires Rust Edition 2021 or earlier to compile. |
| 195 | + |
| 196 | + .. rust-example:: |
| 197 | + :compile_fail: |
| 198 | + :edition: 2024 |
| 199 | + |
| 200 | + // Collides with the C library 'printf' function |
| 201 | + #[export_name = "printf"] // noncompliant |
| 202 | + |
| 203 | + // Missing unsafe marker - noncompliant in Rust 2024 |
| 204 | + #[link_section = ".init_array"] // noncompliant |
| 205 | + static DATA: u32 = 42; // Corrupts initialization table! |
| 206 | + |
| 207 | + fn main() { |
| 208 | + println!("DATA = {DATA}"); |
| 209 | + } |
| 210 | + |
| 211 | + .. compliant_example:: |
| 212 | + :id: compl_ex_xY2zAb3cDe4f |
| 213 | + :status: draft |
| 214 | + |
| 215 | + The ``#[export_name]`` and ``#[link_section]`` attributes must use the ``unsafe()`` wrapper |
| 216 | + to make their safety implications visible. |
| 217 | + |
| 218 | + .. rust-example:: |
| 219 | + :miri: skip |
| 220 | + :edition: 2024 |
| 221 | + |
| 222 | + // SAFETY: 'custom_symbol' does not conflict with any other symbol |
| 223 | + #[unsafe(export_name = "custom_symbol")] |
| 224 | + pub fn my_function() {} |
| 225 | + |
| 226 | + // SAFETY: Placing data in a specific section for embedded systems |
| 227 | + #[unsafe(link_section = ".noinit")] |
| 228 | + static mut PERSISTENT_DATA: [u8; 256] = [0; 256]; |
| 229 | + |
| 230 | + // SAFETY: Custom section for shared memory |
| 231 | + #[unsafe(link_section = ".shared")] |
| 232 | + static SHARED_BUFFER: [u8; 4096] = [0; 4096]; |
| 233 | + |
| 234 | + fn main() { |
| 235 | + my_function(); |
| 236 | + println!("shared buffer size = {}", SHARED_BUFFER.len()); |
| 237 | + unsafe { let _ = PERSISTENT_DATA[0]; } |
| 238 | + } |
| 239 | + |
| 240 | + **Enforcement** |
| 241 | + This guideline can be enforced through the following mechanisms: |
| 242 | + |
| 243 | + * **Rust Edition 2024**: Migrating to Rust Edition 2024 makes violations of this guideline |
| 244 | + compilation errors for ``extern`` blocks and unsafe attributes. |
| 245 | + |
| 246 | + * **Compiler Lints**: Enable the following lints: |
| 247 | + |
| 248 | + * ``#![deny(unsafe_code)]`` - Denies all unsafe code (use ``#[allow(unsafe_code)]`` for justified exceptions) |
| 249 | + * ``#![deny(unsafe_op_in_unsafe_fn)]`` - Requires explicit unsafe blocks within unsafe functions |
| 250 | + * ``#![warn(unsafe_attr_outside_unsafe)]`` - Warns about unsafe attributes without the ``unsafe()`` wrapper (pre-2024) |
| 251 | + |
| 252 | + * **Static Analysis Tools**: |
| 253 | + |
| 254 | + * ``cargo-geiger`` - Counts and reports unsafe code usage |
| 255 | + * ``cargo-audit`` - Checks for known vulnerabilities in dependencies |
| 256 | + * Custom Clippy lints for project-specific requirements |
| 257 | + |
| 258 | + * **Code Review**: Manual review of all code containing ``unsafe`` tokens should be |
| 259 | + part of the development process, with documented justification for each usage. |
| 260 | + |
| 261 | + **Related guidelines** |
| 262 | + * Minimize the scope of unsafe blocks |
| 263 | + * Document safety invariants for all unsafe code with ``// SAFETY:`` comments |
| 264 | + * Prefer safe abstractions over raw unsafe code |
| 265 | + * Use ``#![forbid(unsafe_code)]`` at crate level where possible, with explicit exceptions |
| 266 | + |
| 267 | + .. bibliography:: |
| 268 | + :id: bib_n8YJHvQf4mWx |
| 269 | + :status: draft |
| 270 | + |
| 271 | + .. list-table:: |
| 272 | + :header-rows: 0 |
| 273 | + :widths: auto |
| 274 | + :class: bibliography-table |
| 275 | + |
| 276 | + * - :bibentry:`gui_ZDLZzjeOwLSU:RUST-EDITION-GUIDE` |
| 277 | + - The Rust Edition Guide. "Rust 2024." https://doc.rust-lang.org/edition-guide/rust-2024/index.html. |
| 278 | + * - :bibentry:`gui_ZDLZzjeOwLSU:RUST-REF-UNSAFE-KEYWORD` |
| 279 | + - The Rust Reference. "Unsafe Keyword." https://doc.rust-lang.org/reference/unsafe-keyword.html. |
| 280 | + * - :bibentry:`gui_ZDLZzjeOwLSU:RUST-LINT-UNSAFE` |
| 281 | + - Rust Compiler Lint Documentation. "unsafe_code." https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html#unsafe-code. |
| 282 | + * - :bibentry:`gui_ZDLZzjeOwLSU:RUSTNOMICON-MEET-SAFE` |
| 283 | + - The Rustonomicon. "Meet Safe and Unsafe." https://doc.rust-lang.org/nomicon/meet-safe-and-unsafe.html. |
| 284 | + * - :bibentry:`gui_ZDLZzjeOwLSU:ISO-26262` |
| 285 | + - International Organization for Standardization. "ISO 26262 - Road vehicles - Functional safety." https://www.iso.org/standard/68383.html. |
| 286 | + * - :bibentry:`gui_ZDLZzjeOwLSU:DO-178C` |
| 287 | + - RTCA, Inc. "DO-178C: Software Considerations in Airborne Systems and Equipment Certification." https://store.accuristech.com/standards/rtca-do-178c. |
| 288 | + * - :bibentry:`gui_ZDLZzjeOwLSU:CARGO-GEIGER` |
| 289 | + - cargo-geiger contributors. "cargo-geiger: Detects usage of unsafe Rust." https://github.com/geiger-rs/cargo-geiger. |
| 290 | + * - :bibentry:`gui_ZDLZzjeOwLSU:RUST-REF-EXTERN` |
| 291 | + - The Rust Reference. "External blocks." https://doc.rust-lang.org/reference/items/external-blocks.html. |
| 292 | + * - :bibentry:`gui_ZDLZzjeOwLSU:RUST-REF-UNSAFE-ATTR` |
| 293 | + - The Rust Reference. "Unsafe attributes." https://doc.rust-lang.org/reference/attributes.html#unsafe-attributes. |
| 294 | + * - :bibentry:`gui_ZDLZzjeOwLSU:FERROCENE-SPEC` |
| 295 | + - Ferrocene GmbH. "Ferrocene Language Specification." https://spec.ferrocene.dev/. |
| 296 | + * - :bibentry:`gui_ZDLZzjeOwLSU:RUST-REF-UNION` |
| 297 | + - The Rust Reference. "Unions." https://doc.rust-lang.org/reference/items/unions.html. |
| 298 | + * - :bibentry:`gui_ZDLZzjeOwLSU:UCG-VALIDITY` |
| 299 | + - Rust Unsafe Code Guidelines. "Validity and Safety Invariant." https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#validity-and-safety-invariant. |
0 commit comments