Skip to content

Commit c93b2cb

Browse files
authored
Update types-and-traits.rst
added new guideline on unions
1 parent 587f8bc commit c93b2cb

File tree

1 file changed

+288
-0
lines changed

1 file changed

+288
-0
lines changed

src/coding-guidelines/types-and-traits.rst

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,294 @@
66
Types and Traits
77
================
88

9+
.. SPDX-License-Identifier: MIT OR Apache-2.0
10+
SPDX-FileCopyrightText: The Coding Guidelines Subcommittee Contributors
11+
12+
.. default-domain:: coding-guidelines
13+
14+
.. guideline:: Ensure reads of union fields produce valid values for the field's type
15+
:id: gui_UnionFieldValidity
16+
:category: required
17+
:status: draft
18+
:release: 1.85.0
19+
:decidability: undecidable
20+
:scope: expression
21+
:tags: unions, safety, undefined-behavior
22+
23+
When reading from a union field, ensure that the underlying bytes constitute a valid value
24+
for that field's type.
25+
Reading a union field whose bytes do not represent a valid value
26+
for the field's type is undefined behavior.
27+
28+
Before accessing a union field:
29+
30+
* Verify that the union was last written through that field, or
31+
* Verify that the union was written through a field whose bytes are valid when reinterpreted as the target field's type, or
32+
* Use explicit validity checks when the active field is uncertain
33+
34+
.. rationale::
35+
:id: rat_UnionFieldValidityReason
36+
:status: draft
37+
38+
Unions allow multiple fields to occupy the same memory, similar to C unions.
39+
Unlike enums, unions do not track which field is currently active. It is the programmer's
40+
responsibility to ensure that when a field is read, the underlying bytes are valid for
41+
that field's type [RUST-REF-UNION]_.
42+
43+
Every type has a *validity invariant* — a set of constraints that all values of
44+
that type must satisfy [UCG-VALIDITY]_.
45+
Reading a union field performs a *typed read*,
46+
which asserts that the bytes are valid for the target type.
47+
Violating this invariant is undefined behavior.
48+
49+
Examples of validity requirements for common types:
50+
51+
* **bool**: Must be ``0`` (false) or ``1`` (true). Any other value (e.g., ``3``) is invalid.
52+
* **char**: Must be a valid Unicode scalar value (0x0 to 0xD7FF or 0xE000 to 0x10FFFF).
53+
* **References**: Must be non-null and properly aligned.
54+
* **Enums**: Must hold a valid discriminant value.
55+
* **Floating point**: All bit patterns are valid for the ``f32`` or ``f64``.type
56+
* **Integers**: All bit patterns are valid for integer types.
57+
58+
Consequences of reading invalid values include:
59+
60+
* Immediate undefined behavior, even if the value is not used
61+
* Miscompilation due to compiler assumptions about valid values
62+
* Security vulnerabilities from unexpected program behavior
63+
* Non-deterministic behavior that varies across optimization levels or platforms
64+
65+
.. non_compliant_example::
66+
:id: non_compl_ex_UnionBool
67+
:status: draft
68+
69+
This example reads a boolean from a union field containing an invalid bit pattern.
70+
The value ``3`` is not a valid boolean (only ``0`` and ``1`` are valid).
71+
72+
.. code-block:: rust
73+
74+
union IntOrBool {
75+
i: u8,
76+
b: bool,
77+
}
78+
79+
fn main() {
80+
let u = IntOrBool { i: 3 };
81+
82+
// Noncompliant: reading bool field with invalid value (3)
83+
let invalid_bool = unsafe { u.b }; // UB: 3 is not a valid bool
84+
}
85+
86+
.. non_compliant_example::
87+
:id: non_compl_ex_UnionChar
88+
:status: draft
89+
90+
This example reads a ``char`` from a union containing an invalid Unicode value.
91+
92+
.. code-block:: rust
93+
94+
union IntOrChar {
95+
i: u32,
96+
c: char,
97+
}
98+
99+
fn main() {
100+
// 0xD800 is a surrogate, not a valid Unicode scalar value
101+
let u = IntOrChar { i: 0xD800 };
102+
103+
// Non-compliant: reading char field with invalid Unicode value
104+
let invalid_char = unsafe { u.c }; // UB: surrogates are not valid chars
105+
}
106+
107+
.. non_compliant_example::
108+
:id: non_compl_ex_UnionEnum
109+
:status: draft
110+
111+
This example reads an enum from a ``union`` containing an invalid discriminant.
112+
113+
.. code-block:: rust
114+
115+
#[repr(u8)]
116+
enum Color {
117+
Red = 0,
118+
Green = 1,
119+
Blue = 2,
120+
}
121+
122+
union IntOrColor {
123+
i: u8,
124+
c: Color,
125+
}
126+
127+
fn main() {
128+
let u = IntOrColor { i: 42 };
129+
130+
// Noncompliant: 42 is not a valid Color discriminant
131+
let invalid_color = unsafe { u.c }; // UB: no Color variant for 42
132+
}
133+
134+
.. non_compliant_example::
135+
:id: non_compl_ex_UnionRef
136+
:status: draft
137+
138+
This example reads a reference from a ``union`` containing a null or misaligned pointer.
139+
140+
.. code-block:: rust
141+
142+
union PtrOrRef {
143+
p: *const i32,
144+
r: &'static i32,
145+
}
146+
147+
fn main() {
148+
let u = PtrOrRef { p: std::ptr::null() };
149+
150+
// Non-compliant: null is not a valid reference
151+
let invalid_ref = unsafe { u.r }; // UB: references cannot be null
152+
}
153+
154+
.. compliant_example::
155+
:id: compl_ex_UnionTrackField
156+
:status: draft
157+
158+
Track the active field explicitly to ensure valid reads.
159+
160+
.. code-block:: rust
161+
162+
union IntOrBool {
163+
i: u8,
164+
b: bool,
165+
}
166+
167+
enum ActiveField {
168+
Int,
169+
Bool,
170+
}
171+
172+
struct SafeUnion {
173+
data: IntOrBool,
174+
active: ActiveField,
175+
}
176+
177+
impl SafeUnion {
178+
fn new_int(value: u8) -> Self {
179+
Self {
180+
data: IntOrBool { i: value },
181+
active: ActiveField::Int,
182+
}
183+
}
184+
185+
fn new_bool(value: bool) -> Self {
186+
Self {
187+
data: IntOrBool { b: value },
188+
active: ActiveField::Bool,
189+
}
190+
}
191+
192+
fn get_bool(&self) -> Option<bool> {
193+
match self.active {
194+
// Compliant: only read bool when we know it was written as bool
195+
ActiveField::Bool => Some(unsafe { self.data.b }),
196+
ActiveField::Int => None,
197+
}
198+
}
199+
}
200+
201+
.. compliant_example::
202+
:id: compl_ex_UnionSameField
203+
:status: draft
204+
205+
Read from the same field that was written.
206+
207+
.. code-block:: rust
208+
209+
union IntOrBool {
210+
i: u8,
211+
b: bool,
212+
}
213+
214+
fn main() {
215+
let u = IntOrBool { b: true };
216+
217+
// Compliant: reading the same field that was written
218+
let valid_bool = unsafe { u.b };
219+
println!("bool value: {}", valid_bool);
220+
}
221+
222+
.. compliant_example::
223+
:id: compl_ex_UnionValidReinterpret
224+
:status: draft
225+
226+
Reinterpret between types where all bit patterns are valid.
227+
228+
.. code-block:: rust
229+
230+
union IntBytes {
231+
i: u32,
232+
bytes: [u8; 4],
233+
}
234+
235+
fn main() {
236+
let u = IntBytes { i: 0x12345678 };
237+
238+
// Compliant: all bit patterns are valid for [u8; 4]
239+
let bytes = unsafe { u.bytes };
240+
println!("bytes: {:?}", bytes);
241+
242+
let u2 = IntBytes { bytes: [0x11, 0x22, 0x33, 0x44] };
243+
244+
// Compliant: all bit patterns are valid for u32
245+
let int_value = unsafe { u2.i };
246+
println!("integer: 0x{:08X}", int_value);
247+
}
248+
249+
.. compliant_example::
250+
:id: compl_ex_UnionValidateBool
251+
:status: draft
252+
253+
Validate bytes before reading as a constrained type.
254+
255+
.. code-block:: rust
256+
257+
union IntOrBool {
258+
i: u8,
259+
b: bool,
260+
}
261+
262+
fn try_read_bool(u: &IntOrBool) -> Option<bool> {
263+
// Read as integer first (always valid for u8)
264+
let raw = unsafe { u.i };
265+
266+
// Validate before interpreting as bool
267+
match raw {
268+
0 => Some(false),
269+
1 => Some(true),
270+
_ => None, // Invalid bool value
271+
}
272+
}
273+
274+
fn main() {
275+
let u1 = IntOrBool { i: 1 };
276+
let u2 = IntOrBool { i: 3 };
277+
278+
// Compliant: validates before reading as bool
279+
println!("u1 as bool: {:?}", try_read_bool(&u1)); // Some(true)
280+
println!("u2 as bool: {:?}", try_read_bool(&u2)); // None
281+
}
282+
283+
.. bibliography::
284+
285+
.. [RUST-REF-UNION] The Rust Project Developers. "Rust Reference - Unions."
286+
https://doc.rust-lang.org/reference/items/unions.html
287+
288+
.. [RUST-REF-UB] The Rust Project Developers. "Rust Reference - Behavior considered undefined."
289+
https://doc.rust-lang.org/reference/behavior-considered-undefined.html
290+
291+
.. [UCG-VALIDITY] Rust Unsafe Code Guidelines Working Group. "Validity and Safety Invariant."
292+
https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#validity-and-safety-invariant
293+
294+
.. [RUST-NOMICON-UNION] The Rust Project Developers. "The Rustonomicon - Unions."
295+
https://doc.rust-lang.org/nomicon/unions.html
296+
9297
.. guideline:: Use strong types to differentiate between logically distinct values
10298
:id: gui_xztNdXA2oFNC
11299
:category: advisory

0 commit comments

Comments
 (0)