Skip to content

Commit eb5bdad

Browse files
rcseacordPLeVasseur
authored andcommitted
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.
1 parent 7a9106b commit eb5bdad

1 file changed

Lines changed: 67 additions & 98 deletions

File tree

src/coding-guidelines/attributes/gui_ZDLZzjeOwLSU.rst.inc

Lines changed: 67 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,10 @@
4545
* Prevents accidental use of ``unsafe`` operations without conscious decision
4646
* Aligns with the principle of "defense in depth" in safety-critical systems
4747
48-
* Visibility
49-
50-
* Encouraging visibility discourages hiding unsafe operations inside safe-looking abstractions without proper encapsulation
51-
* Macros that expand to unsafe code should preserve the ``unsafe`` token visibility
52-
5348
* Static Analysis and Tooling
5449
55-
* Tools like ``cargo-geiger`` :cite:`gui_cD3eGh4iJk5l:CARGO-GEIGER`, ``unsafe-inspect``, and custom linters can automatically locate and count unsafe blocks
50+
* Tools like ``cargo-geiger`` :cite:`gui_cD3eGh4iJk5l:CARGO-GEIGER`, ``unsafe-inspect``,
51+
and custom linters can automatically locate and count unsafe blocks
5652
* Enables metrics like "unsafe density" for safety assessments
5753
* Supports qualification evidence required by certification standards
5854
@@ -68,23 +64,43 @@
6864
6965
The ``#[no_mangle]`` attribute is unsafe because it can be used to declare a
7066
function identifier that conflicts with an existing symbol.
71-
This noncompliant example declares an unmangled function named 'malloc'.
72-
This identifier may conflict with the 'malloc' function from C,
73-
if the C library is linked with the executable.
67+
This noncompliant example declares an unmangled function named 'convert'.
68+
This symbol may conflict with another 'convert' symbol defined in a different crate at link time,
69+
resulting in undefined behavior.
70+
It is even possible for an unmangled symbol to conflict with a mangled symbol.
7471
7572
This noncompliant example requires Rust Edition 2021 or earlier to compile.
7673
7774
.. rust-example::
78-
:compile_fail:
75+
.. :compile_fail:
7976
8077
// Undefined behavior by the linker or loader is possible
81-
// if another 'something' function is defined.
78+
// if another 'convert' function is defined.
8279
#[no_mangle]
83-
fn malloc() {}
80+
fn convert() {}
8481
8582
fn main() {
8683
// Call the unmangled function
87-
malloc();
84+
convert();
85+
}
86+
87+
.. compliant_example::
88+
:id: compl_ex_wR1FEyLRKmrr
89+
:status: draft
90+
91+
Rust Edition 2024 enforces that the ``no_mangle`` attribute requires an ``unsafe`` keyword,
92+
as shown in this compliant example.
93+
94+
NOTE: This code can still have undefined behavior if the 'convert' function symbol is define more than once.
95+
96+
.. rust-example::
97+
98+
#[unsafe(no_mangle)] // compliant.
99+
fn convert() {}
100+
101+
fn main() {
102+
// Call the no_mangle function (safe to call)
103+
convert();
88104
}
89105
90106
.. non_compliant_example::
@@ -99,7 +115,7 @@
99115
This noncompliant example requires Rust Edition 2021 or earlier to compile.
100116
101117
.. rust-example::
102-
:compile_fail:
118+
.. :compile_fail:
103119
104120
use std::ffi;
105121
@@ -121,74 +137,21 @@
121137
}
122138
}
123139
124-
.. non_compliant_example::
125-
:id: non_compl_ex_Hk3mNp5qRs7t
126-
:status: draft
127-
128-
The ``#[export_name]`` and ``#[link_section]`` attributes can cause undefined behavior if misused,
129-
as they affect symbol resolution and memory layout at link time.
130-
Without the ``unsafe`` keyword, these hazards are not visible to reviewers or tools.
131-
132-
This noncompliant example has two problems.
133-
First, it uses an ``#[export_name]`` attribute without an unsafe wrapper.
134-
This attribute controls the symbol name used during linking.
135-
If another symbol with the same name exists, it causes undefined behavior.
136-
Rust 2024 requires it to be marked ``unsafe``.
137-
138-
The second problem is that this noncompliant example uses a ``#[link_section]`` attribute without unsafe wrapper.
139-
This attribute places the item in a specific linker section.
140-
Incorrect section placement can cause undefined behavior
141-
(e.g., placing mutable data in read-only sections, or interfering with special sections like ``.init``).
142-
143-
This noncompliant example requires Rust Edition 2021 or earlier to compile.
144-
145-
.. rust-example::
146-
:compile_fail:
147-
148-
// Missing unsafe marker - noncompliant in Rust 2024
149-
#[export_name = "custom_symbol"]
150-
pub fn my_function() {}
151-
152-
// Missing unsafe marker - noncompliant in Rust 2024
153-
#[link_section = ".custom_section"]
154-
static DATA: u32 = 42;
155-
156-
.. non_compliant_example::
157-
:id: non_compl_ex_Jm4oQr6sUv8w
158-
:status: draft
159-
160-
Macros that generate unsafe code without preserving the ``unsafe`` token visibility
161-
obscure safety-critical code from auditors and static analysis tools.
162-
163-
.. rust-example::
164-
165-
// This macro hides the unsafe token from callers - noncompliant
166-
macro_rules! hidden_unsafe_call {
167-
($ptr:expr) => {
168-
unsafe { *$ptr }
169-
};
170-
}
171-
172-
fn main() {
173-
let x = 42;
174-
let ptr = &x as *const i32;
175-
// The unsafe operation is hidden from the caller
176-
let val = hidden_unsafe_call!(ptr);
177-
}
178-
179140
.. compliant_example::
180141
:id: compl_ex_wR1FEyLRKmrq
181142
:status: draft
182143
183-
Using at least Rust Edition 2024 enforces that these things that involve safety obligations require the ``unsafe`` token.
144+
Rust Edition 2024 enforces that ``extern "C"`` blocks require an ``unsafe`` keyword,
145+
as shown in this compliant example.
146+
147+
NOTE: This code can still have undefined behavior if the declared 'malloc' function is incompatible with
148+
the actual definition of the 'malloc' function. To eliminate this undefined behavior, the declaration for
149+
``malloc`` used in this compliant example has been correct to accept one argument of type ``usize``.
184150
185151
.. rust-example::
186152
187153
use std::ffi;
188154
189-
#[unsafe(no_mangle)]
190-
fn something() {}
191-
192155
unsafe extern "C" {
193156
// Here the assumption is that malloc is the one defined by C's stdlib.h
194157
// and that size_of::<usize>() == size_of::<size_t>()
@@ -197,9 +160,6 @@
197160
}
198161

199162
fn main() {
200-
// Call the no_mangle function (safe to call)
201-
something();
202-
203163
// Call the extern functions (requires unsafe block)
204164
unsafe {
205165
let ptr = malloc(1024);
@@ -210,43 +170,52 @@
210170
}
211171
}
212172

213-
.. compliant_example::
214-
:id: compl_ex_xY2zAb3cDe4f
173+
.. non_compliant_example::
174+
:id: non_compl_ex_Hk3mNp5qRs7t
215175
:status: draft
216176

217-
The ``#[export_name]`` and ``#[link_section]`` attributes must use the ``unsafe()`` wrapper
218-
to make their safety implications visible.
177+
The ``#[export_name]`` and ``#[link_section]`` attributes can cause undefined behavior if misused,
178+
as they affect symbol resolution and memory layout at link time.
179+
Without the ``unsafe`` keyword, these hazards are not visible to reviewers or tools.
180+
181+
This noncompliant example has two separate problems.
182+
First, it uses an ``#[export_name]`` attribute without an unsafe wrapper.
183+
This attribute controls the symbol name used during linking.
184+
If another symbol with the same name exists, it causes undefined behavior.
185+
Rust 2024 requires it to be marked ``unsafe``.
186+
187+
The second problem is that this noncompliant example uses a ``#[link_section]`` attribute without unsafe wrapper.
188+
This attribute places the item in a specific linker section.
189+
Incorrect section placement can cause undefined behavior
190+
(e.g., placing mutable data in read-only sections, or interfering with special sections like ``.init``).
191+
192+
This noncompliant example requires Rust Edition 2021 or earlier to compile.
219193

220194
.. rust-example::
195+
:compile_fail:
221196

222-
#[unsafe(export_name = "custom_symbol")]
223-
pub fn my_function() {}
197+
// Missing unsafe marker - noncompliant in Rust 2024
198+
#[export_name = "custom_symbol"] // error: unsafe attribute used without unsafe
199+
pub fn my_function() {}
224200

225-
#[unsafe(link_section = ".custom_section")]
201+
// Missing unsafe marker - noncompliant in Rust 2024
202+
#[link_section = ".custom_section"] // error: unsafe attribute used without unsafe
226203
static DATA: u32 = 42;
227204

228205
.. compliant_example::
229-
:id: compl_ex_gH5iJk6lMn7o
206+
:id: compl_ex_xY2zAb3cDe4f
230207
:status: draft
231208

232-
Macros that involve unsafe operations should require the caller to provide the ``unsafe``
233-
token, preserving visibility for auditors and tools.
209+
The ``#[export_name]`` and ``#[link_section]`` attributes must use the ``unsafe()`` wrapper
210+
to make their safety implications visible.
234211

235212
.. rust-example::
236213

237-
// This macro requires the caller to acknowledge the unsafe operation - compliant
238-
macro_rules! unsafe_deref {
239-
($ptr:expr) => {
240-
*$ptr
241-
};
242-
}
214+
#[unsafe(export_name = "custom_symbol")]
215+
pub fn my_function() {}
243216

244-
fn main() {
245-
let x = 42;
246-
let ptr = &x as *const i32;
247-
// The unsafe operation is visible at the call site
248-
let val = unsafe { unsafe_deref!(ptr) };
249-
}
217+
#[unsafe(link_section = ".custom_section")]
218+
static DATA: u32 = 42;
250219

251220
.. enforcement::
252221
:id: enf_pQ8rSt9uVw0x

0 commit comments

Comments
 (0)