From 7713f0d5195b976cca1ca566c66418053a098e88 Mon Sep 17 00:00:00 2001 From: Pete LeVasseur Date: Tue, 16 Dec 2025 03:30:26 +0900 Subject: [PATCH 1/9] feat: split into own file --- .../values/gui_uyp3mCj77FS8.rst.inc | 203 ++++++++++++++++++ src/coding-guidelines/values/index.rst | 1 + 2 files changed, 204 insertions(+) create mode 100644 src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc 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..c250f0bc --- /dev/null +++ b/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc @@ -0,0 +1,203 @@ +.. SPDX-License-Identifier: MIT OR Apache-2.0 + SPDX-FileCopyrightText: The Coding Guidelines Subcommittee Contributors + +.. default-domain:: coding-guidelines + +.. guideline:: Do not create values from uninitialized memory except for union fields + :id: gui_uyp3mCj77FS8 + :category: mandatory + :status: draft + :release: + :fls: fls_6lg0oaaopc26 + :decidability: undecidable + :scope: system + :tags: undefined-behavior, unsafe + + A program shall not create a value of any type from uninitialized memory, + except when accessing a field of a union type, + where such reads are explicitly defined to be permitted even if the bytes of that field are uninitialized. + It is prohibited to interpret uninitialized memory as a value of any Rust type such as a + primitive, aggregate, reference, pointer, struct, enum, array, or tuple. + + **Exception:** You can access a field of a union even when the backing bytes of that field are uninitialized provided that: + + - The resulting value has an unspecified but well-defined bit pattern. + - Interpreting that value must still comply with the requirements of the accessed type + (e.g., no invalid enum discriminants, no invalid pointer values, etc.). + + For example, reading an uninitialized u32 field of a union is allowed; + reading an uninitialized bool field is disallowed because not all bit patterns are valid. + + .. rationale:: + :id: rat_kjFRrhpS8Wu6 + :status: draft + + Rust's memory model treats all types except unions as having an invariant that all bytes must be initialized before a value may be constructed. + Reading uninitialized memory: + + - creates undefined behavior for most types, + - may violate niche or discriminant validity, + - may create invalid pointer values, or + - may produce values that violate type invariants. + + The sole exception is that unions work like C unions: any union field may be read, even if it was never written. + The resulting bytes must, however, form a valid representation for the field's type, + which is not guaranteed if the union contains arbitrary data. + + .. non_compliant_example:: + :id: non_compl_ex_Qb5GqYTP6db1 + :status: draft + + This noncompliant example creates a value of type ``u32`` from uninitialized memory via + `assume_init `_: + + .. rust-example:: + + use std::mem::MaybeUninit; + + # fn main() { + let x: u32 = unsafe { MaybeUninit::uninit().assume_init() }; // UB + # } + + .. compliant_example:: + :id: compl_ex_Ke869nSXuShU + :status: draft + + Types such as ``u8``, ``u16``, ``u32``, and ``i128`` allow all possible bit patterns. + Provided the memory is initialized, there is no undefined behavior. + + .. rust-example:: + + union U { + n: u32, + bytes: [u8; 4], + } + + # fn main() { + let u = U { bytes: [0xFF, 0xEE, 0xDD, 0xCC] }; + let n = unsafe { u.n }; // OK — all bit patterns valid for u32 + # } + + .. compliant_example:: + :id: compl_ex_Ke869nSXuShV + :status: draft + + This compliant example calls the ``write`` function to fully initialize low-level memory. + + .. rust-example:: + + use std::mem::MaybeUninit; + + # fn main() { + let mut x = MaybeUninit::::uninit(); + x.write(42); + let val = unsafe { x.assume_init() }; // OK — value was fully initialized + # } + + .. non_compliant_example:: + :id: non_compl_ex_Qb5GqYTP6db2 + :status: draft + + Creating a reference from arbitrary or uninitialized bytes is always undefined behavior. + References must be valid, aligned, properly dereferenceable, and non-null. + Uninitialized memory cannot satisfy these invariants. + + .. rust-example:: + + use std::mem::MaybeUninit; + + # fn main() { + let r: &u32 = unsafe { MaybeUninit::uninit().assume_init() }; // UB — invalid reference + # } + + .. non_compliant_example:: + :id: non_compl_ex_Qb5GqYTP6db4 + :status: draft + + Not all bit patterns are valid pointers for all operations (e.g., provenance rules). + You cannot create a pointer from unspecified bytes. + Even a raw pointer type (e.g., ``*const T``) has validity rules. + + .. rust-example:: + + use std::mem::MaybeUninit; + + # fn main() { + let p: *const u32 = unsafe { MaybeUninit::uninit().assume_init() }; // UB + # } + .. non_compliant_example:: + :id: non_compl_ex_Qb5GqYTP6db5 + :status: draft + + Array elements must individually be valid values. + + .. rust-example:: + + use std::mem::MaybeUninit; + + # fn main() { + let mut arr: [MaybeUninit; 4] = unsafe { MaybeUninit::uninit().assume_init() }; + let a = unsafe { std::mem::transmute::<_, [u8; 4]>(arr) }; // UB — not all elements initialized + # } + + .. compliant_example:: + :id: compl_ex_Ke869nSXuShT + :status: draft + + The following code reads a union field: + + .. rust-example:: + + union U { + x: u32, + y: f32, + } + + # fn main() { + let u = U { x: 123 }; // write to one field + let f = unsafe { u.y }; // reading the other field is allowed + # } + + .. non_compliant_example:: + :id: non_compl_ex_Qb5GqYTP6db3 + :status: draft + + Even though unions allow reads of any field, not all bit patterns are valid for a ``bool``. + Unions do not relax type validity requirements. + Only the read itself is allowed; + the resulting bytes must still be a valid bool. + + .. rust-example:: + + union U { + b: bool, + x: u8, + } + + # fn main() { + let u = U { x: 255 }; // 255 is not a valid bool representation + let b = unsafe { u.b }; // UB — invalid bool + # } + + .. compliant_example:: + :id: compl_ex_Ke869nSXuShW + :status: draft + + Accessing padding bytes is allowed if not interpreted as typed data: + + .. rust-example:: + + #[repr(C)] + 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; + let s = unsafe { p.read_unaligned() }; // OK — all *fields* are initialized (padding doesn't matter) + # } 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 From 2ade1b333d6b4e9dbe58ee1032d6880521bd49fb Mon Sep 17 00:00:00 2001 From: "Robert C. Seacord" Date: Mon, 15 Dec 2025 18:23:37 -0500 Subject: [PATCH 2/9] feat: remove description related to union deunionfied --- .../values/gui_uyp3mCj77FS8.rst.inc | 82 ++----------------- 1 file changed, 5 insertions(+), 77 deletions(-) diff --git a/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc b/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc index c250f0bc..6f979401 100644 --- a/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc +++ b/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc @@ -3,7 +3,7 @@ .. default-domain:: coding-guidelines -.. guideline:: Do not create values from uninitialized memory except for union fields +.. guideline:: Do not create values from uninitialized memory :id: gui_uyp3mCj77FS8 :category: mandatory :status: draft @@ -13,21 +13,11 @@ :scope: system :tags: undefined-behavior, unsafe - A program shall not create a value of any type from uninitialized memory, - except when accessing a field of a union type, - where such reads are explicitly defined to be permitted even if the bytes of that field are uninitialized. + A program shall not create a value of any non-``union`` type from uninitialized memory. + Reading from a union is covered by a separate rule, "Do not read from union fields that may contain uninitialized bytes". It is prohibited to interpret uninitialized memory as a value of any Rust type such as a primitive, aggregate, reference, pointer, struct, enum, array, or tuple. - - **Exception:** You can access a field of a union even when the backing bytes of that field are uninitialized provided that: - - - The resulting value has an unspecified but well-defined bit pattern. - - Interpreting that value must still comply with the requirements of the accessed type - (e.g., no invalid enum discriminants, no invalid pointer values, etc.). - - For example, reading an uninitialized u32 field of a union is allowed; - reading an uninitialized bool field is disallowed because not all bit patterns are valid. - + .. rationale:: :id: rat_kjFRrhpS8Wu6 :status: draft @@ -39,11 +29,7 @@ - may violate niche or discriminant validity, - may create invalid pointer values, or - may produce values that violate type invariants. - - The sole exception is that unions work like C unions: any union field may be read, even if it was never written. - The resulting bytes must, however, form a valid representation for the field's type, - which is not guaranteed if the union contains arbitrary data. - + .. non_compliant_example:: :id: non_compl_ex_Qb5GqYTP6db1 :status: draft @@ -59,25 +45,6 @@ let x: u32 = unsafe { MaybeUninit::uninit().assume_init() }; // UB # } - .. compliant_example:: - :id: compl_ex_Ke869nSXuShU - :status: draft - - Types such as ``u8``, ``u16``, ``u32``, and ``i128`` allow all possible bit patterns. - Provided the memory is initialized, there is no undefined behavior. - - .. rust-example:: - - union U { - n: u32, - bytes: [u8; 4], - } - - # fn main() { - let u = U { bytes: [0xFF, 0xEE, 0xDD, 0xCC] }; - let n = unsafe { u.n }; // OK — all bit patterns valid for u32 - # } - .. compliant_example:: :id: compl_ex_Ke869nSXuShV :status: draft @@ -140,45 +107,6 @@ let a = unsafe { std::mem::transmute::<_, [u8; 4]>(arr) }; // UB — not all elements initialized # } - .. compliant_example:: - :id: compl_ex_Ke869nSXuShT - :status: draft - - The following code reads a union field: - - .. rust-example:: - - union U { - x: u32, - y: f32, - } - - # fn main() { - let u = U { x: 123 }; // write to one field - let f = unsafe { u.y }; // reading the other field is allowed - # } - - .. non_compliant_example:: - :id: non_compl_ex_Qb5GqYTP6db3 - :status: draft - - Even though unions allow reads of any field, not all bit patterns are valid for a ``bool``. - Unions do not relax type validity requirements. - Only the read itself is allowed; - the resulting bytes must still be a valid bool. - - .. rust-example:: - - union U { - b: bool, - x: u8, - } - - # fn main() { - let u = U { x: 255 }; // 255 is not a valid bool representation - let b = unsafe { u.b }; // UB — invalid bool - # } - .. compliant_example:: :id: compl_ex_Ke869nSXuShW :status: draft From 8e147c40dffacd550f837f3394fdca6e01b3f73b Mon Sep 17 00:00:00 2001 From: "Robert C. Seacord" Date: Wed, 17 Dec 2025 12:14:44 -0500 Subject: [PATCH 3/9] Update gui_uyp3mCj77FS8.rst.inc had a slightly older version, so I replaced it with the latest. --- .../values/gui_uyp3mCj77FS8.rst.inc | 99 +++++++++++++------ 1 file changed, 71 insertions(+), 28 deletions(-) diff --git a/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc b/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc index 6f979401..b6fe0233 100644 --- a/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc +++ b/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc @@ -13,10 +13,10 @@ :scope: system :tags: undefined-behavior, unsafe - A program shall not create a value of any non-``union`` type from uninitialized memory. - Reading from a union is covered by a separate rule, "Do not read from union fields that may contain uninitialized bytes". - It is prohibited to interpret uninitialized memory as a value of any Rust type such as a - primitive, aggregate, reference, pointer, struct, enum, array, or tuple. + A program shall not create a value of any non-union type from uninitialized memory. + Reading from a union is covered by a separate rule, `Do not read from union fields that may contain uninitialized bytes + `_. + It is prohibited to interpret uninitialized memory as a value of any type. .. rationale:: :id: rat_kjFRrhpS8Wu6 @@ -34,38 +34,45 @@ :id: non_compl_ex_Qb5GqYTP6db1 :status: draft - This noncompliant example creates a value of type ``u32`` from uninitialized memory via + This noncompliant example attempts to create a value of type ``u32`` from uninitialized memory by calling `assume_init `_: .. rust-example:: use std::mem::MaybeUninit; - # fn main() { - let x: u32 = unsafe { MaybeUninit::uninit().assume_init() }; // UB - # } + 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 calls the ``write`` function to fully initialize low-level memory. + This compliant example creates an uninitialized allocation of type ``MaybeUninit``. + The code calls the ``write`` function to write the value 42 into the ``MaybeUninit``. + The call to ``assume_init`` asserts that the value is initialized and extracts the value of type ``u64``. + This is valid because the memory has been initialized by the call to ``write(42)``. + This is the canonical safe pattern for using ``MaybeUninit``. .. rust-example:: use std::mem::MaybeUninit; - # fn main() { - let mut x = MaybeUninit::::uninit(); - x.write(42); - let val = unsafe { x.assume_init() }; // OK — value was fully initialized - # } + 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_Qb5GqYTP6db2 :status: draft - Creating a reference from arbitrary or uninitialized bytes is always undefined behavior. + This noncompliant example creates a reference from uninitialized memory. + Creating a reference from arbitrary or uninitialized bytes is undefined behavior. References must be valid, aligned, properly dereferenceable, and non-null. Uninitialized memory cannot satisfy these invariants. @@ -74,13 +81,15 @@ use std::mem::MaybeUninit; # fn main() { - let r: &u32 = unsafe { MaybeUninit::uninit().assume_init() }; // UB — invalid reference + // Reading an invalid reference is undefined behavior + let r: &u32 = unsafe { MaybeUninit::uninit().assume_init() }; // noncompliant # } .. 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). You cannot create a pointer from unspecified bytes. Even a raw pointer type (e.g., ``*const T``) has validity rules. @@ -89,14 +98,26 @@ use std::mem::MaybeUninit; - # fn main() { - let p: *const u32 = unsafe { MaybeUninit::uninit().assume_init() }; // UB - # } + fn main() { + let p: *const u32 = unsafe { MaybeUninit::uninit().assume_init() }; // noncompliant + } + .. 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 initalized. + + ``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:: @@ -104,28 +125,50 @@ # fn main() { let mut arr: [MaybeUninit; 4] = unsafe { MaybeUninit::uninit().assume_init() }; - let a = unsafe { std::mem::transmute::<_, [u8; 4]>(arr) }; // UB — not all elements initialized + // 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 - Accessing padding bytes is allowed if not interpreted as typed data: + 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:: #[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 + 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; - let s = unsafe { p.read_unaligned() }; // OK — all *fields* are initialized (padding doesn't matter) - # } + 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); + } From 7b27536e7f5c4b154ef77f2170665d3232f3c860 Mon Sep 17 00:00:00 2001 From: "Robert C. Seacord" Date: Wed, 17 Dec 2025 12:37:16 -0500 Subject: [PATCH 4/9] Update gui_uyp3mCj77FS8.rst.inc --- .../values/gui_uyp3mCj77FS8.rst.inc | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc b/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc index b6fe0233..9d6435ee 100644 --- a/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc +++ b/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc @@ -3,7 +3,7 @@ .. default-domain:: coding-guidelines -.. guideline:: Do not create values from uninitialized memory +.. guideline:: Do not read uninitialized memory as a typed value :id: gui_uyp3mCj77FS8 :category: mandatory :status: draft @@ -13,22 +13,19 @@ :scope: system :tags: undefined-behavior, unsafe - A program shall not create a value of any non-union type from uninitialized memory. + Do not read uninitialized memory of any non-union type as a typed value. + 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 a separate rule, `Do not read from union fields that may contain uninitialized bytes `_. - It is prohibited to interpret uninitialized memory as a value of any type. .. rationale:: :id: rat_kjFRrhpS8Wu6 :status: draft - Rust's memory model treats all types except unions as having an invariant that all bytes must be initialized before a value may be constructed. - Reading uninitialized memory: - - - creates undefined behavior for most types, - - may violate niche or discriminant validity, - - may create invalid pointer values, or - - may produce values that violate type invariants. + Rust's memory model requires that all bytes must be initialized before being read as a typed value. + Reading uninitialized memory as a typed value is undefined behavior. .. non_compliant_example:: :id: non_compl_ex_Qb5GqYTP6db1 @@ -80,10 +77,10 @@ use std::mem::MaybeUninit; - # fn main() { - // Reading an invalid reference is undefined behavior - let r: &u32 = unsafe { MaybeUninit::uninit().assume_init() }; // noncompliant - # } + 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_Qb5GqYTP6db4 From 807d57c05ba695f4c47142877520ca94f5df6f03 Mon Sep 17 00:00:00 2001 From: "Robert C. Seacord" Date: Wed, 17 Dec 2025 15:47:40 -0500 Subject: [PATCH 5/9] Update gui_uyp3mCj77FS8.rst.inc --- .../values/gui_uyp3mCj77FS8.rst.inc | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc b/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc index 9d6435ee..ae7ff095 100644 --- a/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc +++ b/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc @@ -19,6 +19,14 @@ Reading from a union is covered by a separate rule, `Do not read from union fields that may contain uninitialized bytes `_. + + Calling `assume_init `_, + or related functions, is treated in the same manner as a typed read. + Calling these function when on memory that is not yet fully initialized causes immediate undefined behavior. + The memory must be properly initialized according to the requirements of the variable’s type. + For example, a variable of reference type must be aligned and non-null. + 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 @@ -31,8 +39,8 @@ :id: non_compl_ex_Qb5GqYTP6db1 :status: draft - This noncompliant example attempts to create a value of type ``u32`` from uninitialized memory by calling - `assume_init `_: + This noncompliant example extracts a value of type ``u32`` from uninitialized memory within a ``MaybeUninit`` container, + which is undefined behavior. .. rust-example:: From 4e170694ff3f80970102328aec6db7c34499cbc9 Mon Sep 17 00:00:00 2001 From: "Robert C. Seacord" Date: Wed, 17 Dec 2025 19:51:42 -0500 Subject: [PATCH 6/9] Update gui_uyp3mCj77FS8.rst.inc added another example and some clarification for references --- .../values/gui_uyp3mCj77FS8.rst.inc | 54 ++++++++++++++++--- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc b/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc index ae7ff095..0574945f 100644 --- a/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc +++ b/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc @@ -24,7 +24,7 @@ or related functions, is treated in the same manner as a typed read. Calling these function when on memory that is not yet fully initialized causes immediate undefined behavior. The memory must be properly initialized according to the requirements of the variable’s type. - For example, a variable of reference type must be aligned and non-null. + 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. @@ -71,6 +71,23 @@ // 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). + You cannot create a pointer from unspecified bytes. + Even a raw pointer type (e.g., ``*const T``) has validity rules. + + .. rust-example:: + + use std::mem::MaybeUninit; + + fn main() { + let p: *const u32 = unsafe { MaybeUninit::uninit().assume_init() }; // noncompliant + } .. non_compliant_example:: :id: non_compl_ex_Qb5GqYTP6db2 @@ -78,7 +95,7 @@ This noncompliant example creates a reference from uninitialized memory. Creating a reference from arbitrary or uninitialized bytes is undefined behavior. - References must be valid, aligned, properly dereferenceable, and non-null. + References must be valid, aligned, dereferenceable, and non-null. Uninitialized memory cannot satisfy these invariants. .. rust-example:: @@ -91,20 +108,41 @@ } .. non_compliant_example:: - :id: non_compl_ex_Qb5GqYTP6db4 + :id: non_compl_ex_Qb5GqYTP6db3 :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). - You cannot create a pointer from unspecified bytes. - Even a raw pointer type (e.g., ``*const T``) has validity rules. + This noncompliant example creates a reference from uninitialized memory. + The ``create_ref`` function has undefined behavior when creating a reference from a dangling pointer. + It creates uninitialized memory sized for a reference (``&u8``). + A reference is essentially a pointer (8 bytes on 64-bit systems). + ``&raw mut uninit`` retreives 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 pointer which is non-null, aligned, but does not point to 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. .. rust-example:: 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 asseting 'uninit' is a valid reference. + let _init = uninit.assume_init(); // noncompliant + } + } fn main() { - let p: *const u32 = unsafe { MaybeUninit::uninit().assume_init() }; // noncompliant + create_ref(); } .. non_compliant_example:: From db75b00d169bdd7be991e7501a3282e8b0898a4e Mon Sep 17 00:00:00 2001 From: "Robert C. Seacord" Date: Wed, 17 Dec 2025 20:10:23 -0500 Subject: [PATCH 7/9] Update gui_uyp3mCj77FS8.rst.inc --- .../values/gui_uyp3mCj77FS8.rst.inc | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc b/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc index 0574945f..91ddcfe6 100644 --- a/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc +++ b/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc @@ -20,9 +20,16 @@ Reading from a union is covered by a separate rule, `Do not read from union fields that may contain uninitialized bytes `_. - Calling `assume_init `_, - or related functions, is treated in the same manner as a typed read. - Calling these function when on memory that is not yet fully initialized causes immediate undefined behavior. + Calling `assume_init `_ 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 when on memory that is not yet fully initialized is undefined behavior. The memory must be properly initialized according to the requirements of the variable’s type. 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``. @@ -166,11 +173,11 @@ use std::mem::MaybeUninit; - # fn main() { - let mut 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 - # } + fn main() { + let mut 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 From 39ff5a09d857e52565c14f065263f4b99d4b7f27 Mon Sep 17 00:00:00 2001 From: "Robert C. Seacord" Date: Wed, 17 Dec 2025 20:14:08 -0500 Subject: [PATCH 8/9] Update gui_uyp3mCj77FS8.rst.inc spelling --- src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc b/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc index 91ddcfe6..4bc3cbc3 100644 --- a/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc +++ b/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc @@ -122,7 +122,7 @@ The ``create_ref`` function has undefined behavior when creating a reference from a dangling pointer. It creates uninitialized memory sized for a reference (``&u8``). A reference is essentially a pointer (8 bytes on 64-bit systems). - ``&raw mut uninit`` retreives a raw mutable pointer to the ``MaybeUninit``. + ``&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 pointer which is non-null, aligned, but does not point to valid, initialized value. @@ -162,7 +162,7 @@ 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 initalized. + 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. From 50101d2a7504983eb3b5bbe6882ae77c5e9861b6 Mon Sep 17 00:00:00 2001 From: "Robert C. Seacord" Date: Thu, 18 Dec 2025 13:56:24 -0500 Subject: [PATCH 9/9] Update src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc Co-authored-by: increasing --- src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc b/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc index 4bc3cbc3..607e87ae 100644 --- a/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc +++ b/src/coding-guidelines/values/gui_uyp3mCj77FS8.rst.inc @@ -143,7 +143,7 @@ unsafe { // write non-null and aligned address. (&raw mut uninit).cast::<*const u8>().write(ptr::dangling()); - // Undefined behavior occurs when asseting 'uninit' is a valid reference. + // Undefined behavior occurs when asserting 'uninit' is a valid reference. let _init = uninit.assume_init(); // noncompliant } }