cmov/macros.rs
1//! Macro definitions.
2
3/// Generates a mask the width of the given unsigned integer type `$uint` if the input value is
4/// non-zero.
5///
6/// Uses `core::hint::black_box` to coerce our desired codegen based on real-world observations
7/// of the assembly generated by Rust/LLVM.
8///
9/// Implemented as a macro instead of a generic function because it uses functionality for which
10/// there aren't available `core` traits, e.g. `wrapping_neg`.
11///
12/// See also:
13/// - CVE-2026-23519
14/// - RustCrypto/utils#1332
15macro_rules! masknz {
16 ($value:tt : $uint:ident) => {{
17 let mut value: $uint = $value;
18 value |= value.wrapping_neg(); // has MSB `1` if non-zero, `0` if zero
19
20 // use `black_box` to obscure we're computing a 1-bit value
21 core::hint::black_box(
22 value >> ($uint::BITS - 1), // Extract MSB
23 )
24 .wrapping_neg() // Generate $uint::MAX mask if `black_box` outputs `1`
25 }};
26}
27
28#[cfg(test)]
29mod tests {
30 // Spot check up to a given limit
31 const TEST_LIMIT: u8 = 128;
32
33 macro_rules! masknz_test {
34 ( $($name:ident : $uint:ident),+ ) => {
35 $(
36 #[test]
37 fn $name() {
38 assert_eq!(masknz!(0: $uint), 0);
39
40 // Test lower values
41 for i in 1..=$uint::from(TEST_LIMIT) {
42 assert_eq!(masknz!(i: $uint), $uint::MAX);
43 }
44
45 // Test upper values
46 for i in ($uint::MAX - $uint::from(TEST_LIMIT))..=$uint::MAX {
47 assert_eq!(masknz!(i: $uint), $uint::MAX);
48 }
49 }
50 )+
51 }
52 }
53
54 // Ensure the macro works with any types we might use it with (we only use u8, u32, and u64)
55 masknz_test!(
56 masknz_u8: u8,
57 masknz_u16: u16,
58 masknz_u32: u32,
59 masknz_u64: u64,
60 masknz_u128: u128,
61 masknz_usize: usize
62 );
63}