diff --git a/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc b/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc new file mode 100644 index 00000000..b20fb8fc --- /dev/null +++ b/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc @@ -0,0 +1,278 @@ +.. SPDX-License-Identifier: MIT OR Apache-2.0 + SPDX-FileCopyrightText: The Coding Guidelines Subcommittee Contributors + +.. default-domain:: coding-guidelines + +.. guideline:: Do not read uninitialized memory of any non-union type as a typed value + :id: gui_uyp3mCj77FS8 + :category: mandatory + :status: draft + :release: + :fls: fls_6lg0oaaopc26 + :decidability: undecidable + :scope: system + :tags: undefined-behavior, unsafe + + Do not read uninitialized memory of any non-union type as a typed value :cite:`gui_uyp3mCj77FS8:RUSTNOMICON-UNINIT`. + This is sometimes referred to as *transmuting* or *read-at-type*. + Memory can remain uninitialized if it is not read as a type. + + Reading from a union is covered by `Do not read from union fields that may contain uninitialized bytes + `_. + + Calling :std:`std::mem::MaybeUninit::assume_init` + :cite:`gui_uyp3mCj77FS8:MAYBEUNINIT-DOC` or any of the following related functions is treated in the same manner as a typed read: + + * ``assume_init_drop`` + * ``assume_init_mut`` + * ``assume_init_read`` + * ``assume_init_ref`` + * ``array_assume_init`` + + Calling any of these function on memory that is not yet fully initialized is undefined behavior :cite:`gui_uyp3mCj77FS8:RUST-REF-BEHAVIOR`. + The memory must be properly initialized according to the requirements of the variable's type :cite:`gui_uyp3mCj77FS8:UCG-VALIDITY`. + For example, a variable of reference type must be aligned, non-null, and point to valid memory. + Similarly, entirely uninitialized memory may have any content, while a ``bool`` must always be ``true`` or ``false``. + Consequently, reading an uninitialized ``bool`` is undefined behavior. + + .. rationale:: + :id: rat_kjFRrhpS8Wu6 + :status: draft + + Rust's memory model requires that all bytes must be initialized before being read as a typed value + :cite:`gui_uyp3mCj77FS8:RUSTNOMICON-UNINIT` :cite:`gui_uyp3mCj77FS8:FERROCENE-SPEC`. + Reading uninitialized memory as a typed value is undefined behavior :cite:`gui_uyp3mCj77FS8:RUST-REF-BEHAVIOR`. + This guideline aligns with functional safety standards :cite:`gui_uyp3mCj77FS8:ISO-26262` + :cite:`gui_uyp3mCj77FS8:IEC-61508` and secure coding practices. + + .. non_compliant_example:: + :id: non_compl_ex_Qb5GqYTP6db1 + :status: draft + + This noncompliant example extracts a value of type ``u32`` from uninitialized memory within a ``MaybeUninit`` container, + which is undefined behavior. + + .. rust-example:: + :miri: expect_ub + :warn: allow + + use std::mem::MaybeUninit; + + fn main() { + // Reading uninitialized memory as a typed value is undefined behavior + let _x: u32 = unsafe { MaybeUninit::uninit().assume_init() }; // noncompliant + } + + .. compliant_example:: + :id: compl_ex_Ke869nSXuShV + :status: draft + + This compliant example creates an uninitialized variable ``x`` of type ``MaybeUninit``. + The code calls the ``write`` function to write the value 42 to ``x``. + The call to ``assume_init`` asserts that the value is initialized and extracts the value of type ``u64``. + This call is compliant with this rule because the memory has been properly initialized by the call to ``write(42)``. + This is the canonical safe pattern for using ``MaybeUninit``. + + .. rust-example:: + :miri: + + use std::mem::MaybeUninit; + + fn main() { + let mut x = MaybeUninit::::uninit(); + x.write(42); + // x is fully initialized + let _val = unsafe { x.assume_init() }; // compliant + } + + .. non_compliant_example:: + :id: non_compl_ex_Qb5GqYTP6db4 + :status: draft + + This noncompliant example creates a pointer from uninitialized memory. + Not all bit patterns are valid pointers for all operations (e.g., provenance rules) :cite:`gui_uyp3mCj77FS8:UCG-VALIDITY`. + You cannot create a pointer from unspecified bytes. + Even a raw pointer type (e.g., ``*const T``) has validity rules :cite:`gui_uyp3mCj77FS8:RUST-REF-BEHAVIOR`. + + .. rust-example:: + :miri: expect_ub + :warn: allow + + use std::mem::MaybeUninit; + + fn main() { + let _p: *const u32 = unsafe { MaybeUninit::uninit().assume_init() }; // noncompliant + } + + .. non_compliant_example:: + :id: non_compl_ex_Qb5GqYTP6db2 + :status: draft + + This noncompliant example creates a reference from uninitialized memory. + Creating a reference from arbitrary or uninitialized bytes is undefined behavior :cite:`gui_uyp3mCj77FS8:RUST-REF-BEHAVIOR`. + References must be valid, aligned, dereferenceable, and non-null :cite:`gui_uyp3mCj77FS8:UCG-VALIDITY`. + Uninitialized memory cannot satisfy these requirements. + + .. rust-example:: + :miri: expect_ub + :warn: allow + + use std::mem::MaybeUninit; + + fn main() { + // Reading an invalid reference is undefined behavior + let _r: &u32 = unsafe { MaybeUninit::uninit().assume_init() }; // noncompliant + } + + .. non_compliant_example:: + :id: non_compl_ex_Qb5GqYTP6db3 + :status: draft + + This noncompliant example creates a reference from uninitialized memory. + + .. rust-example:: + :miri: expect_ub + + use std::mem::MaybeUninit; + use std::ptr; + + fn create_ref() { + let mut uninit: MaybeUninit<&u8> = MaybeUninit::uninit(); + unsafe { + // write non-null and aligned address. + (&raw mut uninit).cast::<*const u8>().write(ptr::dangling()); + // Undefined behavior occurs when asserting 'uninit' is a valid reference. + let _init = uninit.assume_init(); // noncompliant + } + } + + fn main() { + create_ref(); + } + + The ``create_ref`` function has undefined behavior when creating a reference from a dangling pointer. + It creates uninitialized memory sized for a reference (``&u8``). + ``&raw mut uninit`` retrieves a raw mutable pointer to the ``MaybeUninit``. + The ``.cast::<*const u8>()`` reinterprets it as a pointer to a raw pointer. + The call to ``.write(ptr::dangling())`` writes a dangling pointer value. + This creates a non-null, aligned pointer that does not point to a valid, initialized value. + The call to ``assume_init`` asserts the ``MaybeUninit<&u8>`` is a valid ``&u8`` reference + A reference (``&T``) has stricter requirements than a raw pointer. + Even though the bit pattern looks like a valid pointer, + the semantic requirements for a reference are violated. + The compiler is allowed to assume references always point to valid data, + so this can cause miscompilation. + + .. non_compliant_example:: + :id: non_compl_ex_Qb5GqYTP6db5 + :status: draft + + Array elements must individually be valid values. + This noncompliant example creates an uninitialized array of four ``u8`` values. + The call to ``.assume_init`` asserting that the array is initialized is valid here because + an array of ``MaybeUninit`` can contain uninitialized bytes. + The call to ``std::mem::transmute`` reinterprets the ``[MaybeUninit; 4]`` as ``[u8; 4]``. + This is undefined behavior, because the bytes were never initialized. + Even though all bit patterns (0-255) are valid for the ``u8`` type, the values must be initialized. + + ``MaybeUninit`` can hold uninitialized memory — that's its purpose. + ``u8`` cannot hold uninitialized memory — all 8 bits must be defined. + The ``transmute`` performs a typed read that asserts the bytes are valid ``u8`` values. + Reading uninitialized bytes as a concrete type is always undefined behavior. + + .. rust-example:: + :miri: expect_ub + + use std::mem::MaybeUninit; + + fn main() { + let arr: [MaybeUninit; 4] = unsafe { MaybeUninit::uninit().assume_init() }; + // Undefined behavior constructing an array of 'u8' from uninitialized memory. + let _a = unsafe { std::mem::transmute::<_, [u8; 4]>(arr) }; // noncompliant + } + + .. compliant_example:: + :id: compl_ex_Ke869nSXuShW + :status: draft + + This compliant example defines a C-layout ``struct`` with: + + * ``a``: 1 byte at offset 0 + * 3 bytes of padding (to align ``b`` to 4 bytes) + * ``b``: 4 bytes at offset 4 + * Total size: 8 bytes + + The variable ``buf`` is a fully, zero-initialized 8-byte buffer. + + The first wo bytes of ``buf`` are overwritten. + The byte buffer ``buf`` pointer is cast to a pointer to ``S``. + The call to ``read_unaligned`` reads the ``struct`` without requiring alignment. + + This example is compliant because: + + * All bytes are initialized (buffer was zero-initialized) + * All fields have valid values (``u8`` and ``u32`` accept any bit pattern) + * Padding bytes don't need to be any specific value + * ``read_unaligned`` handles the alignment issue + + .. rust-example:: + :miri: + + #[repr(C)] + #[derive(Debug)] + struct S { + a: u8, + b: u32, + } + + fn main() { + let mut buf = [0u8; std::mem::size_of::()]; + buf[0] = 10; + buf[1] = 20; // writing padding is fine + + let p = buf.as_ptr() as *const S; + // All fields are initialized (padding doesn't matter) + let s = unsafe { p.read_unaligned() }; // compliant + println!("{:?}", s); + } + .. bibliography:: + :id: bib_WNCi5njUWLuY + :status: draft + + .. list-table:: + :header-rows: 0 + :widths: auto + :class: bibliography-table + + * - :bibentry:`gui_uyp3mCj77FS8:DO-178C` + - RTCA, Inc. "DO-178C: Software Considerations in Airborne Systems and Equipment Certification." https://store.accuristech.com/standards/rtca-do-178c?product_id=2200105. + + * - :bibentry:`gui_uyp3mCj77FS8:RUSTNOMICON-UNINIT` + - The Rust Project Developers. "The Rustonomicon - Uninitialized Memory." https://doc.rust-lang.org/nomicon/uninitialized.html. + + * - :bibentry:`gui_uyp3mCj77FS8:RUST-REF-BEHAVIOR` + - The Rust Project Developers. "The Rust Reference - Behavior considered undefined." https://doc.rust-lang.org/reference/behavior-considered-undefined.html. + + * - :bibentry:`gui_uyp3mCj77FS8:MAYBEUNINIT-DOC` + - The Rust Project Developers. "std::mem::MaybeUninit - Rust Standard Library Documentation." https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html. + + * - :bibentry:`gui_uyp3mCj77FS8:FERROCENE-SPEC` + - Ferrocene GmbH. "Ferrocene Language Specification." https://spec.ferrocene.dev/. + + * - :bibentry:`gui_uyp3mCj77FS8:MISRA-RUST` + - MISRA Consortium Limited. "MISRA Rust Guidelines (Draft)." https://misra.org.uk/. + + * - :bibentry:`gui_uyp3mCj77FS8:ISO-26262` + - International Organization for Standardization. "ISO 26262 - Road vehicles - Functional safety." https://www.iso.org/standard/68383.html. + + * - :bibentry:`gui_uyp3mCj77FS8:IEC-61508` + - International Electrotechnical Commission. "IEC 61508 - Functional Safety of Electrical/Electronic/Programmable Electronic Safety-related Systems." https://www.iec.ch/functional-safety. + + * - :bibentry:`gui_uyp3mCj77FS8:RUST-SAFETY-CRITICAL-WG` + - Rust Foundation. "Rust Safety-Critical Consortium." https://github.com/rust-lang/safety-critical-consortium. + + * - :bibentry:`gui_uyp3mCj77FS8:UCG-VALIDITY` + - Rust Unsafe Code Guidelines. "Validity and Safety Invariant." https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#validity-and-safety-invariant. + + * - :bibentry:`gui_uyp3mCj77FS8:CERT-RUST` + - Carnegie Mellon University Software Engineering Institute. "SEI CERT Rust Coding Standard." https://wiki.sei.cmu.edu/confluence/display/rust diff --git a/src/coding-guidelines/values/index.rst b/src/coding-guidelines/values/index.rst index c2419fc7..16eb6636 100644 --- a/src/coding-guidelines/values/index.rst +++ b/src/coding-guidelines/values/index.rst @@ -6,3 +6,4 @@ Values ====== +.. include:: gui_uyp3mCj77FS8.rst.inc