|
1 | 1 | .. SPDX-License-Identifier: MIT OR Apache-2.0 |
2 | 2 | SPDX-FileCopyrightText: The Coding Guidelines Subcommittee Contributors |
3 | 3 |
|
4 | | -.. default-domain:: coding-guidelines |
| 4 | +.. guideline:: Do not hide unsafe code in macros |
| 5 | + :id: gui_yQC2tSE2Qeii |
| 6 | + :category: advisory |
| 7 | + :status: draft |
| 8 | + :release: unclear-latest |
| 9 | + :fls: fls_4vjbkm4ceymk |
| 10 | + :decidability: decidable |
| 11 | + :scope: module |
| 12 | + :tags: safety, reduce-human-error |
5 | 13 |
|
6 | | -.. guideline:: Do not hide unsafe blocks within macro expansions |
7 | | - :id: gui_FRLaMIMb4t3S |
8 | | - :category: required |
9 | | - :status: draft |
10 | | - :release: todo |
11 | | - :fls: fls_4vjbkm4ceymk |
12 | | - :decidability: todo |
13 | | - :scope: todo |
14 | | - :tags: reduce-human-error |
| 14 | + Do not hide unsafe code in macro definitions. |
| 15 | + Macros that expand to unsafe code should preserve the ``unsafe`` token visibility. |
15 | 16 |
|
16 | | - Description of the guideline goes here. |
| 17 | + .. rationale:: |
| 18 | + :id: rat_s7ffMlPUFt77 |
| 19 | + :status: draft |
17 | 20 |
|
18 | | - .. rationale:: |
19 | | - :id: rat_WJubG7KuUDLW |
20 | | - :status: draft |
| 21 | + Macros that generate unsafe code obscure safety-critical code, making the code more difficult to review and audit. |
21 | 22 |
|
22 | | - Explanation of why this guideline is important. |
| 23 | + .. non_compliant_example:: |
| 24 | + :id: non_compl_ex_YNX7AnWENTu7 |
| 25 | + :status: draft |
23 | 26 |
|
24 | | - .. non_compliant_example:: |
25 | | - :id: non_compl_ex_AyFnP0lJLHxi |
26 | | - :status: draft |
| 27 | + Macros that generate unsafe code without preserving the ``unsafe`` token visibility obscure safety-critical code. |
| 28 | + This noncompliant example defines a ``deref_ptr`` macro that performs an unsafe poitner deference. |
27 | 29 |
|
28 | | - Explanation of code example. |
| 30 | + .. rust-example:: |
29 | 31 |
|
30 | | - .. rust-example:: |
| 32 | + // This macro hides the unsafe token from callers - noncompliant |
| 33 | + macro_rules! deref_ptr { |
| 34 | + ($ptr:expr) => { |
| 35 | + unsafe { *$ptr } |
| 36 | + }; |
| 37 | + } |
31 | 38 |
|
32 | | - #[allow(dead_code)] |
33 | | - fn example_function() { |
34 | | - // Non-compliant implementation |
35 | | - } |
36 | | - # |
37 | | - # fn main() {} |
| 39 | + fn main() { |
| 40 | + let x = 42; |
| 41 | + let ptr = &x as *const i32; |
| 42 | + // The unsafe operation is hidden from the caller |
| 43 | + println!("val = {}", deref_ptr!(ptr)); |
| 44 | + } |
38 | 45 |
|
39 | | - .. compliant_example:: |
40 | | - :id: compl_ex_pO5gP1aj2v4F |
41 | | - :status: draft |
42 | 46 |
|
43 | | - Explanation of code example. |
| 47 | + .. compliant_example:: |
| 48 | + :id: compl_ex_nBkfzueTWvTA |
| 49 | + :status: draft |
44 | 50 |
|
45 | | - .. rust-example:: |
| 51 | + This compliant example requires the caller to wrap the macro invocation in an ``unsafe`` block, |
| 52 | + making the use of unsafe code obvious at the call site. |
| 53 | + Visibility can be further improved by prefixing the identifier for each unsafe macro with the string “unsafe\_” |
46 | 54 |
|
47 | | - #[allow(dead_code)] |
48 | | - fn example_function() { |
49 | | - // Compliant implementation |
50 | | - } |
51 | | - # |
52 | | - # fn main() {} |
| 55 | + .. rust-example:: |
| 56 | + |
| 57 | + // This macro requires the caller to acknowledge the unsafe operation - compliant |
| 58 | + macro_rules! unsafe_deref_ptr { |
| 59 | + ($ptr:expr) => { |
| 60 | + *$ptr |
| 61 | + }; |
| 62 | + } |
| 63 | + |
| 64 | + fn main() { |
| 65 | + let x = 42; |
| 66 | + let ptr = &x as *const i32; |
| 67 | + // The unsafe operation is visible at the call site |
| 68 | + let val = unsafe { unsafe_deref_ptr!(ptr) }; |
| 69 | + println!("val = {}", val); |
| 70 | + } |
0 commit comments