|
45 | 45 | * Prevents accidental use of ``unsafe`` operations without conscious decision |
46 | 46 | * Aligns with the principle of "defense in depth" in safety-critical systems |
47 | 47 |
|
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 | | -
|
53 | 48 | * Static Analysis and Tooling |
54 | 49 |
|
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 |
56 | 52 | * Enables metrics like "unsafe density" for safety assessments |
57 | 53 | * Supports qualification evidence required by certification standards |
58 | 54 |
|
|
68 | 64 |
|
69 | 65 | The ``#[no_mangle]`` attribute is unsafe because it can be used to declare a |
70 | 66 | 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. |
74 | 71 |
|
75 | 72 | This noncompliant example requires Rust Edition 2021 or earlier to compile. |
76 | 73 |
|
77 | 74 | .. rust-example:: |
78 | | - :compile_fail: |
| 75 | +.. :compile_fail: |
79 | 76 |
|
80 | 77 | // Undefined behavior by the linker or loader is possible |
81 | | - // if another 'something' function is defined. |
| 78 | + // if another 'convert' function is defined. |
82 | 79 | #[no_mangle] |
83 | | - fn malloc() {} |
| 80 | + fn convert() {} |
84 | 81 |
|
85 | 82 | fn main() { |
86 | 83 | // 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(); |
88 | 104 | } |
89 | 105 |
|
90 | 106 | .. non_compliant_example:: |
|
99 | 115 | This noncompliant example requires Rust Edition 2021 or earlier to compile. |
100 | 116 |
|
101 | 117 | .. rust-example:: |
102 | | - :compile_fail: |
| 118 | +.. :compile_fail: |
103 | 119 |
|
104 | 120 | use std::ffi; |
105 | 121 |
|
|
121 | 137 | } |
122 | 138 | } |
123 | 139 |
|
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 | | -
|
179 | 140 | .. compliant_example:: |
180 | 141 | :id: compl_ex_wR1FEyLRKmrq |
181 | 142 | :status: draft |
182 | 143 |
|
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``. |
184 | 150 |
|
185 | 151 | .. rust-example:: |
186 | 152 |
|
187 | 153 | use std::ffi; |
188 | 154 |
|
189 | | - #[unsafe(no_mangle)] |
190 | | - fn something() {} |
191 | | -
|
192 | 155 | unsafe extern "C" { |
193 | 156 | // Here the assumption is that malloc is the one defined by C's stdlib.h |
194 | 157 | // and that size_of::<usize>() == size_of::<size_t>() |
|
197 | 160 | } |
198 | 161 |
|
199 | 162 | fn main() { |
200 | | - // Call the no_mangle function (safe to call) |
201 | | - something(); |
202 | | - |
203 | 163 | // Call the extern functions (requires unsafe block) |
204 | 164 | unsafe { |
205 | 165 | let ptr = malloc(1024); |
|
210 | 170 | } |
211 | 171 | } |
212 | 172 |
|
213 | | - .. compliant_example:: |
214 | | - :id: compl_ex_xY2zAb3cDe4f |
| 173 | + .. non_compliant_example:: |
| 174 | + :id: non_compl_ex_Hk3mNp5qRs7t |
215 | 175 | :status: draft |
216 | 176 |
|
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. |
219 | 193 |
|
220 | 194 | .. rust-example:: |
| 195 | + :compile_fail: |
221 | 196 |
|
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() {} |
224 | 200 |
|
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 |
226 | 203 | static DATA: u32 = 42; |
227 | 204 |
|
228 | 205 | .. compliant_example:: |
229 | | - :id: compl_ex_gH5iJk6lMn7o |
| 206 | + :id: compl_ex_xY2zAb3cDe4f |
230 | 207 | :status: draft |
231 | 208 |
|
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. |
234 | 211 |
|
235 | 212 | .. rust-example:: |
236 | 213 |
|
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() {} |
243 | 216 |
|
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; |
250 | 219 |
|
251 | 220 | .. enforcement:: |
252 | 221 | :id: enf_pQ8rSt9uVw0x |
|
0 commit comments