-
Notifications
You must be signed in to change notification settings - Fork 28
[Coding Guideline]: Ensure reads of union fields produce valid values #300
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
manhatsu
wants to merge
21
commits into
rustfoundation:main
Choose a base branch
from
rcseacord:doc/union-field-validation
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+291
−0
Open
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
04c578b
Update types-and-traits.rst
rcseacord 499e31c
Apply suggestion from @rcseacord
rcseacord 46312c5
Update types-and-traits.rst
rcseacord 4c9eb08
Update types-and-traits.rst
rcseacord 4de7d26
fix: add bibliography entry;
manhatsu 0ce4556
chore: put citations in table
manhatsu 20d0418
fix: implement "Copy" to use enum in union
manhatsu 32de3d5
fix: add main function
manhatsu a3035a8
Update types-and-traits.rst
rcseacord 5f36ca3
Update types-and-traits.rst
rcseacord 9b3b574
Update types-and-traits.rst
rcseacord 60fd543
Update types-and-traits.rst
rcseacord 2e94771
Update types-and-traits.rst
rcseacord d74513f
Update types-and-traits.rst
rcseacord 4b2ef9f
chore: enable line breaks
manhatsu f4813ca
Update gui_UnionFieldValidity.rst.inc
rcseacord 8c78d43
Update src/coding-guidelines/types-and-traits/gui_UnionFieldValidity.…
rcseacord 5ad6d18
Remove git cruft
PLeVasseur b25c300
Update src/coding-guidelines/types-and-traits/gui_UnionFieldValidity.…
rcseacord 21a9c52
chore: rename with unique ID
696e633
fix: bibliography style
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
290 changes: 290 additions & 0 deletions
290
src/coding-guidelines/types-and-traits/gui_0cuTYG8RVYjg.rst.inc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,290 @@ | ||
| .. SPDX-License-Identifier: MIT OR Apache-2.0 | ||
| SPDX-FileCopyrightText: The Coding Guidelines Subcommittee Contributors | ||
|
|
||
| .. default-domain:: coding-guidelines | ||
|
|
||
| .. guideline:: Ensure reads of union fields produce valid values for the field's type | ||
| :id: gui_0cuTYG8RVYjg | ||
| :category: required | ||
| :status: draft | ||
| :release: unknown | ||
| :fls: fls_oFIRXBPXu6Zv | ||
| :decidability: undecidable | ||
| :scope: system | ||
| :tags: defect, safety, undefined-behavior | ||
|
|
||
| Ensure that the underlying bytes constitute a valid value for that field's type when reading from a union field. | ||
| Reading a union field whose bytes do not represent a valid value for the field's type is undefined behavior. | ||
|
|
||
| Before accessing a union field, verify that that the union was either: | ||
|
|
||
| * last written through that field, or | ||
| * written through a field whose bytes are valid when reinterpreted as the target field's type | ||
|
|
||
| If the active field is uncertain, use explicit validity checks. | ||
|
|
||
| .. rationale:: | ||
| :id: rat_UnionFieldValidityReason | ||
| :status: draft | ||
|
|
||
| Similar to C, unions allow multiple fields to occupy the same memory. | ||
| Unlike enumeration types, unions do not track which field is currently active. | ||
| You must ensure that when a field is read that | ||
| the underlying bytes are valid for that field's type :cite:`gui_0cuTYG8RVYjg:RUST-REF-UNION`. | ||
|
|
||
| Every type has a *validity invariant* — a set of constraints that all values of | ||
| that type must satisfy :cite:`gui_0cuTYG8RVYjg:UCG-VALIDITY`. | ||
| Reading a union field performs a *typed read*, | ||
| which asserts that the bytes are valid for the target type. | ||
|
|
||
| Examples of validity requirements for common types: | ||
|
|
||
| * **bool**: Must be ``0`` (false) or ``1`` (true). Any other value (e.g., ``3``) is invalid. | ||
| * **char**: Must be a valid Unicode scalar value (``0x0`` to ``0xD7FF`` or ``0xE000`` to ``0x10FFFF``). | ||
| * **References**: Must be non-null and properly aligned. | ||
| * **Enums**: Must hold a valid discriminant value. | ||
| * **Floating point**: All bit patterns are valid for the ``f32`` or ``f64`` types. | ||
| * **Integers**: All bit patterns are valid for integer types. | ||
|
|
||
| Reading an invalid value is undefined behavior. | ||
|
|
||
| .. non_compliant_example:: | ||
| :id: non_compl_ex_UnionBool | ||
| :status: draft | ||
|
|
||
| This noncompliant example reads an invalid bit pattern from a Boolean union field. | ||
| The value ``3`` is not a valid value of type ``bool`` (only ``0`` and ``1`` are valid). | ||
|
|
||
| .. code-block:: rust | ||
|
|
||
| union IntOrBool { | ||
| i: u8, | ||
| b: bool, | ||
| } | ||
|
|
||
| fn main() { | ||
| let u = IntOrBool { i: 3 }; | ||
|
|
||
| // Undefined behavior reading an invalid value from a union field of type 'bool' | ||
| unsafe { u.b }; // Noncompliant | ||
| } | ||
|
|
||
| .. non_compliant_example:: | ||
| :id: non_compl_ex_UnionChar | ||
| :status: draft | ||
|
|
||
| This noncompliant example reads an invalid Unicode value from a ``union`` field of type ``char`` . | ||
|
|
||
| .. code-block:: rust | ||
|
|
||
| union IntOrChar { | ||
| i: u32, | ||
| c: char, | ||
| } | ||
|
|
||
| fn main() { | ||
| // '0xD800' is a surrogate and not a valid Unicode scalar value | ||
| let u = IntOrChar { i: 0xD800 }; | ||
|
|
||
| // Reading an invalid Unicode value from a union field of type 'char' | ||
| unsafe { u.c }; // Noncompliant | ||
| } | ||
|
|
||
| .. non_compliant_example:: | ||
| :id: non_compl_ex_UnionEnum | ||
| :status: draft | ||
|
|
||
| This noncompliant example reads an invalid discriminant from a union field of 'Color' enumeration type. | ||
|
|
||
| .. code-block:: rust | ||
|
|
||
| #[repr(u8)] | ||
| #[derive(Copy, Clone)] | ||
| enum Color { | ||
| Red = 0, | ||
| Green = 1, | ||
| Blue = 2, | ||
| } | ||
|
|
||
| union IntOrColor { | ||
| i: u8, | ||
| c: Color, | ||
| } | ||
|
|
||
| fn main() { | ||
| let u = IntOrColor { i: 42 }; | ||
|
|
||
| // Undefined behavior reading an invalid discriminant from the 'Color' enumeration type | ||
| unsafe { u.c }; // Noncompliant | ||
| } | ||
|
|
||
| .. non_compliant_example:: | ||
| :id: non_compl_ex_UnionRef | ||
| :status: draft | ||
|
|
||
| This noncompliant example reads a reference from a union containing a null pointer. | ||
| A similar problem occurs when reading a misaligned pointer. | ||
|
|
||
| .. code-block:: rust | ||
|
|
||
| union PtrOrRef { | ||
| p: *const i32, | ||
| r: &'static i32, | ||
| } | ||
|
|
||
| fn main() { | ||
| let u = PtrOrRef { p: std::ptr::null() }; | ||
|
|
||
| // Undefined behavior reading a null value from a reference field of a union | ||
| unsafe { u.r }; // Noncompliant | ||
| } | ||
|
|
||
| .. compliant_example:: | ||
| :id: compl_ex_UnionTrackField | ||
| :status: draft | ||
|
|
||
| This compliant example tracks the active field explicitly to ensure valid reads. | ||
|
|
||
| .. code-block:: rust | ||
|
|
||
| union IntOrBool { | ||
| i: u8, | ||
| b: bool, | ||
| } | ||
|
|
||
| enum ActiveField { | ||
| Int, | ||
| Bool, | ||
| } | ||
|
|
||
| struct SafeUnion { | ||
| data: IntOrBool, | ||
| active: ActiveField, | ||
| } | ||
|
|
||
| impl SafeUnion { | ||
| fn new_int(value: u8) -> Self { | ||
| Self { | ||
| data: IntOrBool { i: value }, | ||
| active: ActiveField::Int, | ||
| } | ||
| } | ||
|
|
||
| fn new_bool(value: bool) -> Self { | ||
| Self { | ||
| data: IntOrBool { b: value }, | ||
| active: ActiveField::Bool, | ||
| } | ||
| } | ||
|
|
||
| fn get_bool(&self) -> Option<bool> { | ||
| match self.active { | ||
| // Compliant: only read bool when we know it was written as bool | ||
| ActiveField::Bool => Some(unsafe { self.data.b }), | ||
| ActiveField::Int => None, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| fn main() { | ||
| let union_bool = SafeUnion::new_bool(true); | ||
| let union_int = SafeUnion::new_int(42); | ||
|
|
||
| println!("Bool union as bool: {:?}", union_bool.get_bool()); // Some(true) | ||
| println!("Int union as bool: {:?}", union_int.get_bool()); // None | ||
| } | ||
|
|
||
| .. compliant_example:: | ||
| :id: compl_ex_UnionSameField | ||
| :status: draft | ||
|
|
||
| This compliant solution reads from the same field that was written. | ||
|
|
||
| .. code-block:: rust | ||
|
|
||
| union IntOrBool { | ||
| i: u8, | ||
| b: bool, | ||
| } | ||
|
|
||
| fn main() { | ||
| let u = IntOrBool { b: true }; | ||
|
|
||
| // Read the same field that was written | ||
| println!("bool value: {}", unsafe { u.b }); // compliant | ||
| } | ||
|
|
||
| .. compliant_example:: | ||
| :id: compl_ex_UnionValidReinterpret | ||
| :status: draft | ||
|
|
||
| This compliant example reinterprets the value as a different types where all bit patterns are valid. | ||
|
|
||
| .. code-block:: rust | ||
|
|
||
| union IntBytes { | ||
| i: u32, | ||
| bytes: [u8; 4], | ||
| } | ||
|
|
||
| fn main() { | ||
| let u = IntBytes { i: 0x12345678 }; | ||
|
|
||
| // All bit patterns are valid for [u8; 4] | ||
| println!("bytes: {:?}", unsafe { u.bytes }); // compliant | ||
|
|
||
| let u2 = IntBytes { bytes: [0x11, 0x22, 0x33, 0x44] }; | ||
|
|
||
| // All bit patterns are valid for 'u32' | ||
| println!("integer: 0x{:08X}", unsafe { u2.i }); // compliant | ||
| } | ||
|
|
||
| .. compliant_example:: | ||
| :id: compl_ex_UnionValidateBool | ||
| :status: draft | ||
|
|
||
| This compliant example validates bytes before reading as a constrained type. | ||
|
|
||
| .. code-block:: rust | ||
|
|
||
| union IntOrBool { | ||
| i: u8, | ||
| b: bool, | ||
| } | ||
|
|
||
| fn try_read_bool(u: &IntOrBool) -> Option<bool> { | ||
| // Read as integer (always valid for 'u8') | ||
| let raw = unsafe { u.i }; | ||
|
|
||
| // Validate before interpreting as a value of type 'bool' | ||
| match raw { | ||
| 0 => Some(false), | ||
| 1 => Some(true), | ||
| _ => None, // Invalid Boolean value | ||
| } | ||
| } | ||
|
|
||
| fn main() { | ||
| let u1 = IntOrBool { i: 1 }; | ||
| let u2 = IntOrBool { i: 3 }; | ||
|
|
||
| // Validates before reading as value of type 'bool' | ||
| println!("u1 as bool: {:?}", try_read_bool(&u1)); // Some(true) | ||
| println!("u2 as bool: {:?}", try_read_bool(&u2)); // None | ||
| } | ||
|
|
||
| .. bibliography:: | ||
| :id: bib_WNCi5njUWLuZ | ||
| :status: draft | ||
|
|
||
| .. list-table:: | ||
| :header-rows: 0 | ||
| :widths: auto | ||
| :class: bibliography-table | ||
|
|
||
| * - :bibentry:`gui_0cuTYG8RVYjg:RUST-REF-UNION` | ||
| - The Rust Reference. "Unions." https://doc.rust-lang.org/reference/items/unions.html. | ||
|
|
||
| * - :bibentry:`gui_0cuTYG8RVYjg:UCG-VALIDITY` | ||
| - Rust Unsafe Code Guidelines. "Validity and Safety Invariant." https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#validity-and-safety-invariant. | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,3 +7,4 @@ Types and Traits | |
| ================ | ||
|
|
||
| .. include:: gui_xztNdXA2oFNC.rst.inc | ||
| .. include:: gui_0cuTYG8RVYjg.rst.inc | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.