Skip to content

Commit 1c7d6bb

Browse files
[Coding Rule]: assure visibility of unsafe keyword in unsafe code (#358)
* Add guideline: assure visibility of unsafe tokens in unsafe code * Revise guideline for unsafe token visibility Updated guideline to ensure visibility of unsafe tokens in unsafe code, reflecting changes in Rust Edition 2024. Added rationale and examples to clarify requirements. * Revise guideline for 'unsafe' keyword visibility Update guideline to ensure visibility of 'unsafe' keyword in unsafe code, reflecting changes in Rust Edition 2024. * Fix formatting and update Rust examples in guidelines * Update guidelines on unsafe attribute usage in Rust Clarify issues with using #[export_name] and #[link_section] attributes without unsafe wrappers in Rust 2024. * Refactor guidelines for unsafe operations in Rust Removed visibility section and updated examples for compliance with Rust Edition 2024. Clarified unsafe usage in function declarations and attributes. * Update guidelines for unsafe attributes in Rust Removed exceptions section and added safety notes for unsafe attributes. * fix: make unsafe attribute example 2024 compile-fail * fix: convert unsafe keyword guideline to .rst --------- Co-authored-by: Pete LeVasseur <[email protected]>
1 parent 2f51959 commit 1c7d6bb

File tree

2 files changed

+306
-0
lines changed

2 files changed

+306
-0
lines changed
Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
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.

src/coding-guidelines/attributes/index.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,10 @@
55

66
Attributes
77
==========
8+
9+
.. toctree::
10+
:maxdepth: 1
11+
:titlesonly:
12+
:glob:
13+
14+
gui_*

0 commit comments

Comments
 (0)