From 590a0c0b6da428239b33d0673da93deea3bce470 Mon Sep 17 00:00:00 2001 From: manhatsu Date: Thu, 4 Dec 2025 15:22:14 +0900 Subject: [PATCH 1/5] doc: prohibit creating value from uninitialized memory --- src/coding-guidelines/values.rst | 61 ++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/coding-guidelines/values.rst b/src/coding-guidelines/values.rst index 2fa02de0..397dc45e 100644 --- a/src/coding-guidelines/values.rst +++ b/src/coding-guidelines/values.rst @@ -5,3 +5,64 @@ Values ====== + +.. 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 + + 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 (primitive, aggregate, reference, pointer, struct, enum, array, tuple, etc.) + + **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 + + The following code creates a value from uninitialized memory via assume_init: + + .. code-block:: rust + + use std::mem::MaybeUninit; + + let x: u32 = unsafe { MaybeUninit::uninit().assume_init() }; // UB + + .. compliant_example:: + :id: compl_ex_Ke869nSXuShT + :status: draft + + The following code reads a union field: + + .. code-block:: rust + + union U { + x: u32, + y: f32, + } + + let u = U { x: 123 }; // write to one field + let f = unsafe { u.y }; // reading the other field is allowed From ab3bd77d493eeef71f3d0041cb21f853c30e3133 Mon Sep 17 00:00:00 2001 From: "Robert C. Seacord" Date: Thu, 4 Dec 2025 09:59:24 -0500 Subject: [PATCH 2/5] Revise uninitialized memory guidelines and examples Updated guidelines on uninitialized memory usage and added examples of compliant and non-compliant code. --- src/coding-guidelines/values.rst | 89 ++++++++++++++++++++++++++++---- 1 file changed, 79 insertions(+), 10 deletions(-) diff --git a/src/coding-guidelines/values.rst b/src/coding-guidelines/values.rst index 397dc45e..49ed31ab 100644 --- a/src/coding-guidelines/values.rst +++ b/src/coding-guidelines/values.rst @@ -14,36 +14,44 @@ Values :fls: fls_6lg0oaaopc26 :decidability: undecidable :scope: system - :tags: undefined-behavior + :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 (primitive, aggregate, reference, pointer, struct, enum, array, tuple, etc.) + 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.). + - 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. + 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: + 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. + - 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. + 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 - The following code creates a value from uninitialized memory via assume_init: + This noncompliant example creates a value from uninitialized memory via ``assume_init``: .. code-block:: rust @@ -51,6 +59,47 @@ Values let x: u32 = unsafe { MaybeUninit::uninit().assume_init() }; // UB + .. 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. + + .. code-block:: rust + + use std::mem::MaybeUninit; + + 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. + + .. code-block:: rust + + use std::mem::MaybeUninit; + + 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. + + .. code-block:: rust + + use std::mem::MaybeUninit; + + 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 @@ -66,3 +115,23 @@ Values 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. + + .. code-block:: rust + + union U { + b: bool, + x: u8, + } + + let u = U { x: 255 }; // 255 is not a valid bool representation + let b = unsafe { u.b }; // UB — invalid bool + From 57f303a23428cc94c08a90a38a2634fb0a983fb6 Mon Sep 17 00:00:00 2001 From: "Robert C. Seacord" Date: Thu, 4 Dec 2025 11:18:38 -0500 Subject: [PATCH 3/5] Enhance values.rst with additional noncompliant and compliant examples Added noncompliant and compliant examples demonstrating safe /unsafe memory initialization in Rust. --- src/coding-guidelines/values.rst | 54 +++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/src/coding-guidelines/values.rst b/src/coding-guidelines/values.rst index 49ed31ab..1502eb6e 100644 --- a/src/coding-guidelines/values.rst +++ b/src/coding-guidelines/values.rst @@ -51,7 +51,8 @@ Values :id: non_compl_ex_Qb5GqYTP6db1 :status: draft - This noncompliant example creates a value from uninitialized memory via ``assume_init``: + This noncompliant example creates a value of type ``u32`` from uninitialized memory via + `assume_init `_: .. code-block:: rust @@ -59,6 +60,37 @@ Values 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. + + .. code-block:: rust + + union U { + n: u32, + bytes: [u8; 4], + } + + 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 calles the ``write`` function to fully initialize low-level memory. + + .. code-block:: rust + + use std::mem::MaybeUninit; + + 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 @@ -135,3 +167,23 @@ Values 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_Ke869nSXuShT + :status: draft + + Accessing padding bytes is allowed if not interpreted as typed data: + + .. code-block:: rust + + #[repr(C)] + struct S { + a: u8, + b: u32, + } + + 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) From a4783cc9369f8749e333cf88677e450910e0a245 Mon Sep 17 00:00:00 2001 From: manhatsu Date: Fri, 5 Dec 2025 09:01:51 +0900 Subject: [PATCH 4/5] fix: use valid tags --- src/coding-guidelines/values.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coding-guidelines/values.rst b/src/coding-guidelines/values.rst index 1502eb6e..5d618388 100644 --- a/src/coding-guidelines/values.rst +++ b/src/coding-guidelines/values.rst @@ -14,7 +14,7 @@ Values :fls: fls_6lg0oaaopc26 :decidability: undecidable :scope: system - :tags: undefined-behavior, unsafe + :tags: undefined-behavior, safety, security A program shall not create a value of any type from uninitialized memory, except when accessing a field of a union type, From f348e065f420f77028182966f3df87d87bf53d8b Mon Sep 17 00:00:00 2001 From: manhatsu Date: Fri, 5 Dec 2025 09:03:44 +0900 Subject: [PATCH 5/5] fix: avoid hash collision --- src/coding-guidelines/values.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coding-guidelines/values.rst b/src/coding-guidelines/values.rst index 5d618388..fb40e2d5 100644 --- a/src/coding-guidelines/values.rst +++ b/src/coding-guidelines/values.rst @@ -168,7 +168,7 @@ Values let b = unsafe { u.b }; // UB — invalid bool .. compliant_example:: - :id: compl_ex_Ke869nSXuShT + :id: compl_ex_5Rs1t5gYgZLr :status: draft Accessing padding bytes is allowed if not interpreted as typed data: