Skip to content

Commit b3a22ad

Browse files
authored
cmov: impl Cmov(Eq) for u16 using masking (#1345)
Uses the same strategy as the `u32` and `u64` impls, but: - For `Cmov`, uses `masknz32` to compute mask, then truncates to `u16` - For `CmovEq`, XORs the inputs, widens to 32-bits, and calls the `maskne32`/`maskeq32` functions like the 32-bit impl, where both then truncate the mask to 8-bits for selecting a `Condition`, which is performed using `masksel`
1 parent ced2f2a commit b3a22ad

1 file changed

Lines changed: 27 additions & 23 deletions

File tree

cmov/src/portable.rs

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,25 @@
1-
//! Portable "best effort" implementation of `Cmov`.
1+
//! Portable "best effort" implementation of `Cmov`/`CmovEq`.
22
//!
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.
57
//!
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.
810
911
use crate::{Cmov, CmovEq, Condition};
1012
use core::ops::{BitAnd, BitOr, Not};
1113

12-
// Uses `Cmov` impl for `u32`
1314
impl Cmov for u16 {
1415
#[inline]
1516
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);
1918
}
2019

2120
#[inline]
2221
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);
2623
}
2724
}
2825

@@ -50,40 +47,47 @@ impl Cmov for u64 {
5047
}
5148
}
5249

53-
// Uses `CmovEq` impl for `u32`
5450
impl CmovEq for u16 {
5551
#[inline]
5652
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+
);
5858
}
5959

6060
#[inline]
6161
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+
);
6367
}
6468
}
6569

6670
impl CmovEq for u32 {
6771
#[inline]
6872
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);
7074
}
7175

7276
#[inline]
7377
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);
7579
}
7680
}
7781

7882
impl CmovEq for u64 {
7983
#[inline]
8084
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);
8286
}
8387

8488
#[inline]
8589
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);
8791
}
8892
}
8993

@@ -137,10 +141,10 @@ fn masknz32(condition: u32) -> u32 {
137141
let mut mask = condition;
138142
unsafe {
139143
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),
144148
);
145149
}
146150
mask

0 commit comments

Comments
 (0)