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}