|
1 | | -//! Portable "best effort" implementation of `Cmov`. |
| 1 | +//! Portable "best effort" implementation of `Cmov`/`CmovEq`. |
2 | 2 | //! |
3 | | -//! This implementation is based on portable bitwise arithmetic but cannot guarantee that the |
4 | | -//! resulting generated assembly is free of branch instructions. |
| 3 | +//! This implementation is based on portable bitwise arithmetic augmented with tactical usage of |
| 4 | +//! `core::hint::black_box` based on past observations of where the optimizer has inserted branches |
| 5 | +//! (see CVE-2026-23519), but the fully portable implementation cannot guarantee that the resulting |
| 6 | +//! generated assembly is free of branch instructions. |
5 | 7 | //! |
6 | | -//! For select platforms we use `asm!` for mask generation which should largely mitigate the |
7 | | -//! optimizer potentially inserting branches. |
| 8 | +//! For select platforms using this backend (currently limited to ARM32) we use `asm!` for mask |
| 9 | +//! generation which should largely mitigate the optimizer potentially inserting branches. |
8 | 10 |
|
9 | 11 | use crate::{Cmov, CmovEq, Condition}; |
10 | 12 | use core::ops::{BitAnd, BitOr, Not}; |
11 | 13 |
|
12 | | -// Uses `Cmov` impl for `u32` |
13 | 14 | impl Cmov for u16 { |
14 | 15 | #[inline] |
15 | 16 | fn cmovnz(&mut self, value: &u16, condition: Condition) { |
16 | | - let mut tmp = u32::from(*self); |
17 | | - tmp.cmovnz(&(*value).into(), condition); |
18 | | - *self = (tmp & 0xFFFF) as u16; |
| 17 | + *self = masksel(*self, *value, (masknz32(condition.into()) & 0xFFFF) as u16); |
19 | 18 | } |
20 | 19 |
|
21 | 20 | #[inline] |
22 | 21 | fn cmovz(&mut self, value: &u16, condition: Condition) { |
23 | | - let mut tmp = u32::from(*self); |
24 | | - tmp.cmovz(&(*value).into(), condition); |
25 | | - *self = (tmp & 0xFFFF) as u16; |
| 22 | + *self = masksel(*self, *value, (!masknz32(condition.into()) & 0xFFFF) as u16); |
26 | 23 | } |
27 | 24 | } |
28 | 25 |
|
@@ -50,40 +47,47 @@ impl Cmov for u64 { |
50 | 47 | } |
51 | 48 | } |
52 | 49 |
|
53 | | -// Uses `CmovEq` impl for `u32` |
54 | 50 | impl CmovEq for u16 { |
55 | 51 | #[inline] |
56 | 52 | fn cmovne(&self, rhs: &Self, input: Condition, output: &mut Condition) { |
57 | | - u32::from(*self).cmovne(&(*rhs).into(), input, output); |
| 53 | + *output = masksel( |
| 54 | + *output, |
| 55 | + input, |
| 56 | + (masknz32((*self ^ *rhs).into()) & 0xFF) as Condition, |
| 57 | + ); |
58 | 58 | } |
59 | 59 |
|
60 | 60 | #[inline] |
61 | 61 | fn cmoveq(&self, rhs: &Self, input: Condition, output: &mut Condition) { |
62 | | - u32::from(*self).cmoveq(&(*rhs).into(), input, output); |
| 62 | + *output = masksel( |
| 63 | + *output, |
| 64 | + input, |
| 65 | + (!masknz32((*self ^ *rhs).into()) & 0xFF) as Condition, |
| 66 | + ); |
63 | 67 | } |
64 | 68 | } |
65 | 69 |
|
66 | 70 | impl CmovEq for u32 { |
67 | 71 | #[inline] |
68 | 72 | fn cmovne(&self, rhs: &Self, input: Condition, output: &mut Condition) { |
69 | | - *output = masksel(*output, input, (maskne32(*self, *rhs) & 0xFF) as u8); |
| 73 | + *output = masksel(*output, input, (maskne32(*self, *rhs) & 0xFF) as Condition); |
70 | 74 | } |
71 | 75 |
|
72 | 76 | #[inline] |
73 | 77 | fn cmoveq(&self, rhs: &Self, input: Condition, output: &mut Condition) { |
74 | | - *output = masksel(*output, input, (maskeq32(*self, *rhs) & 0xFF) as u8); |
| 78 | + *output = masksel(*output, input, (maskeq32(*self, *rhs) & 0xFF) as Condition); |
75 | 79 | } |
76 | 80 | } |
77 | 81 |
|
78 | 82 | impl CmovEq for u64 { |
79 | 83 | #[inline] |
80 | 84 | fn cmovne(&self, rhs: &Self, input: Condition, output: &mut Condition) { |
81 | | - *output = masksel(*output, input, (maskne64(*self, *rhs) & 0xFF) as u8); |
| 85 | + *output = masksel(*output, input, (maskne64(*self, *rhs) & 0xFF) as Condition); |
82 | 86 | } |
83 | 87 |
|
84 | 88 | #[inline] |
85 | 89 | fn cmoveq(&self, rhs: &Self, input: Condition, output: &mut Condition) { |
86 | | - *output = masksel(*output, input, (maskeq64(*self, *rhs) & 0xFF) as u8); |
| 90 | + *output = masksel(*output, input, (maskeq64(*self, *rhs) & 0xFF) as Condition); |
87 | 91 | } |
88 | 92 | } |
89 | 93 |
|
@@ -137,10 +141,10 @@ fn masknz32(condition: u32) -> u32 { |
137 | 141 | let mut mask = condition; |
138 | 142 | unsafe { |
139 | 143 | core::arch::asm!( |
140 | | - "rsbs {0}, {0}, #0", // Reverse subtract |
141 | | - "sbcs {0}, {0}, {0}", // Subtract with carry, setting flags |
142 | | - inout(reg) mask, |
143 | | - options(nostack, nomem), |
| 144 | + "rsbs {0}, {0}, #0", // Reverse subtract |
| 145 | + "sbcs {0}, {0}, {0}", // Subtract with carry, setting flags |
| 146 | + inout(reg) mask, |
| 147 | + options(nostack, nomem), |
144 | 148 | ); |
145 | 149 | } |
146 | 150 | mask |
|
0 commit comments