Skip to content

Latest commit

 

History

History
76 lines (60 loc) · 2.38 KB

File metadata and controls

76 lines (60 loc) · 2.38 KB
.. default-domain:: coding-guidelines

Do not hide unsafe code in macros

.. guideline:: Do not hide unsafe code in macros
   :id: gui_FRLaMIMb4t3S
   :category: advisory
   :status: draft
   :release: unclear-latest
   :fls: fls_4vjbkm4ceymk
   :decidability: decidable
   :scope: module
   :tags: safety, reduce-human-error

   Do not hide unsafe code in macro definitions.
   Macros that expand to unsafe code should preserve the ``unsafe`` token visibility.

   .. rationale::
      :id: rat_s7ffMlPUFt77
      :status: draft

      Macros that generate unsafe code obscure safety-critical code, making the code more difficult to review and audit.

   .. non_compliant_example::
      :id: non_compl_ex_YNX7AnWENTu7
      :status: draft

      Macros that generate unsafe code without preserving the ``unsafe`` token visibility obscure safety-critical code.
      This noncompliant example defines a ``deref_ptr`` macro that performs an unsafe pointer dereference.

      .. rust-example::
        :miri:

        // This macro hides the unsafe token from callers - noncompliant
        macro_rules! deref_ptr {
            ($ptr:expr) => {
                unsafe { *$ptr }
            };
        }

        fn main() {
            let x = 42;
            let ptr = &x as *const i32;
            // The unsafe operation is hidden from the caller
            println!("val = {}", deref_ptr!(ptr));
        }

   .. compliant_example::
      :id: compl_ex_nBkfzueTWvTA
      :status: draft

      This compliant example requires the caller to wrap the macro invocation in an ``unsafe`` block,
      making the use of unsafe code obvious at the call site.
      Visibility can be further improved by prefixing the identifier for each unsafe macro with the string ``unsafe_``.

      .. rust-example::
        :miri:

        // This macro requires the caller to acknowledge the unsafe operation - compliant
        macro_rules! unsafe_deref_ptr {
            ($ptr:expr) => {
                *$ptr
            };
        }

        fn main() {
            let x = 42;
            let ptr = &x as *const i32;
            // The unsafe operation is visible at the call site
            let val = unsafe { unsafe_deref_ptr!(ptr) };
            println!("val = {}", val);
        }