Skip to content

Commit 734c63b

Browse files
authored
cmov: implement constant-time equality comparisons (#873)
1 parent 83c7093 commit 734c63b

File tree

5 files changed

+475
-116
lines changed

5 files changed

+475
-116
lines changed

cmov/src/aarch64.rs

Lines changed: 68 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,112 @@
1-
use crate::{Cmov, Condition};
1+
use crate::{Cmov, CmovEq, Condition};
22
use core::arch::asm;
33

44
macro_rules! csel {
5-
($cmp:expr, $csel:expr, $dst:expr, $src:expr, $condition:expr) => {
5+
($csel:expr, $dst:expr, $src:expr, $condition:expr) => {
66
unsafe {
77
asm! {
8-
$cmp,
8+
"cmp {0:w}, 0",
99
$csel,
1010
in(reg) $condition,
1111
inlateout(reg) *$dst,
12-
in(reg) $src,
12+
in(reg) *$src,
1313
in(reg) *$dst,
1414
options(pure, nomem, nostack),
1515
};
1616
}
1717
};
1818
}
1919

20+
macro_rules! csel_eq {
21+
($instruction:expr, $lhs:expr, $rhs:expr, $condition:expr, $dst:expr) => {
22+
let mut tmp = *$dst as u16;
23+
unsafe {
24+
asm! {
25+
"eor {0:w}, {1:w}, {2:w}",
26+
"cmp {0:w}, 0",
27+
$instruction,
28+
out(reg) _,
29+
in(reg) *$lhs,
30+
in(reg) *$rhs,
31+
inlateout(reg) tmp,
32+
in(reg) $condition as u16,
33+
in(reg) tmp,
34+
options(pure, nomem, nostack),
35+
};
36+
};
37+
38+
*$dst = tmp as u8;
39+
};
40+
}
41+
2042
impl Cmov for u16 {
2143
#[inline(always)]
2244
fn cmovnz(&mut self, value: &Self, condition: Condition) {
23-
csel!(
24-
"cmp {0:w}, 0",
25-
"csel {1:w}, {2:w}, {3:w}, NE",
26-
self,
27-
*value,
28-
condition
29-
);
45+
csel!("csel {1:w}, {2:w}, {3:w}, NE", self, value, condition);
3046
}
3147

3248
#[inline(always)]
3349
fn cmovz(&mut self, value: &Self, condition: Condition) {
34-
csel!(
35-
"cmp {0:w}, 0",
36-
"csel {1:w}, {2:w}, {3:w}, EQ",
37-
self,
38-
*value,
39-
condition
40-
);
50+
csel!("csel {1:w}, {2:w}, {3:w}, EQ", self, value, condition);
51+
}
52+
}
53+
54+
impl CmovEq for u16 {
55+
#[inline(always)]
56+
fn cmovne(&self, rhs: &Self, input: Condition, output: &mut Condition) {
57+
csel_eq!("csel {3:w}, {4:w}, {5:w}, NE", self, rhs, input, output);
58+
}
59+
60+
#[inline(always)]
61+
fn cmoveq(&self, rhs: &Self, input: Condition, output: &mut Condition) {
62+
csel_eq!("csel {3:w}, {4:w}, {5:w}, EQ", self, rhs, input, output);
4163
}
4264
}
4365

4466
impl Cmov for u32 {
4567
#[inline(always)]
4668
fn cmovnz(&mut self, value: &Self, condition: Condition) {
47-
csel!(
48-
"cmp {0:w}, 0",
49-
"csel {1:w}, {2:w}, {3:w}, NE",
50-
self,
51-
*value,
52-
condition
53-
);
69+
csel!("csel {1:w}, {2:w}, {3:w}, NE", self, value, condition);
5470
}
5571

5672
#[inline(always)]
5773
fn cmovz(&mut self, value: &Self, condition: Condition) {
58-
csel!(
59-
"cmp {0:w}, 0",
60-
"csel {1:w}, {2:w}, {3:w}, EQ",
61-
self,
62-
*value,
63-
condition
64-
);
74+
csel!("csel {1:w}, {2:w}, {3:w}, EQ", self, value, condition);
75+
}
76+
}
77+
78+
impl CmovEq for u32 {
79+
#[inline(always)]
80+
fn cmovne(&self, rhs: &Self, input: Condition, output: &mut Condition) {
81+
csel_eq!("csel {3:w}, {4:w}, {5:w}, NE", self, rhs, input, output);
82+
}
83+
84+
#[inline(always)]
85+
fn cmoveq(&self, rhs: &Self, input: Condition, output: &mut Condition) {
86+
csel_eq!("csel {3:w}, {4:w}, {5:w}, EQ", self, rhs, input, output);
6587
}
6688
}
6789

6890
impl Cmov for u64 {
6991
#[inline(always)]
7092
fn cmovnz(&mut self, value: &Self, condition: Condition) {
71-
csel!(
72-
"cmp {0:x}, 0",
73-
"csel {1:x}, {2:x}, {3:x}, NE",
74-
self,
75-
*value,
76-
condition
77-
);
93+
csel!("csel {1:x}, {2:x}, {3:x}, NE", self, value, condition);
7894
}
7995

8096
#[inline(always)]
8197
fn cmovz(&mut self, value: &Self, condition: Condition) {
82-
csel!(
83-
"cmp {0:x}, 0",
84-
"csel {1:x}, {2:x}, {3:x}, EQ",
85-
self,
86-
*value,
87-
condition
88-
);
98+
csel!("csel {1:x}, {2:x}, {3:x}, EQ", self, value, condition);
99+
}
100+
}
101+
102+
impl CmovEq for u64 {
103+
#[inline(always)]
104+
fn cmovne(&self, rhs: &Self, input: Condition, output: &mut Condition) {
105+
csel_eq!("csel {3:w}, {4:w}, {5:w}, NE", self, rhs, input, output);
106+
}
107+
108+
#[inline(always)]
109+
fn cmoveq(&self, rhs: &Self, input: Condition, output: &mut Condition) {
110+
csel_eq!("csel {3:w}, {4:w}, {5:w}, EQ", self, rhs, input, output);
89111
}
90112
}

cmov/src/lib.rs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ mod x86;
2222
pub type Condition = u8;
2323

2424
/// Conditional move
25-
// TODO(tarcieri): make one of `cmovz`/`cmovnz` a provided method which calls the other?
2625
pub trait Cmov {
2726
/// Move if non-zero.
2827
///
@@ -41,6 +40,25 @@ pub trait Cmov {
4140
}
4241
}
4342

43+
/// Conditional move with equality comparison
44+
pub trait CmovEq {
45+
/// Move if both inputs are equal.
46+
///
47+
/// Uses a `xor` instruction to compare the two values, and
48+
/// conditionally moves `input` to `output` when they are equal.
49+
fn cmoveq(&self, rhs: &Self, input: Condition, output: &mut Condition);
50+
51+
/// Move if both inputs are not equal.
52+
///
53+
/// Uses a `xor` instruction to compare the two values, and
54+
/// conditionally moves `input` to `output` when they are not equal.
55+
fn cmovne(&self, rhs: &Self, input: Condition, output: &mut Condition) {
56+
let mut tmp = 1u8;
57+
self.cmoveq(rhs, 0u8, &mut tmp);
58+
tmp.cmoveq(&1u8, input, output);
59+
}
60+
}
61+
4462
impl Cmov for u8 {
4563
#[inline(always)]
4664
fn cmovnz(&mut self, value: &Self, condition: Condition) {
@@ -57,6 +75,18 @@ impl Cmov for u8 {
5775
}
5876
}
5977

78+
impl CmovEq for u8 {
79+
#[inline(always)]
80+
fn cmoveq(&self, rhs: &Self, input: Condition, output: &mut Condition) {
81+
(*self as u16).cmoveq(&(*rhs as u16), input, output);
82+
}
83+
84+
#[inline(always)]
85+
fn cmovne(&self, rhs: &Self, input: Condition, output: &mut Condition) {
86+
(*self as u16).cmovne(&(*rhs as u16), input, output);
87+
}
88+
}
89+
6090
impl Cmov for u128 {
6191
#[inline(always)]
6292
fn cmovnz(&mut self, value: &Self, condition: Condition) {
@@ -80,3 +110,27 @@ impl Cmov for u128 {
80110
*self = (lo as u128) | (hi as u128) << 64;
81111
}
82112
}
113+
114+
impl CmovEq for u128 {
115+
#[inline(always)]
116+
fn cmovne(&self, rhs: &Self, input: Condition, output: &mut Condition) {
117+
let lo = (*self & u64::MAX as u128) as u64;
118+
let hi = (*self >> 64) as u64;
119+
120+
let mut tmp = 1u8;
121+
lo.cmovne(&((*rhs & u64::MAX as u128) as u64), 0, &mut tmp);
122+
hi.cmovne(&((*rhs >> 64) as u64), 0, &mut tmp);
123+
tmp.cmoveq(&0, input, output);
124+
}
125+
126+
#[inline(always)]
127+
fn cmoveq(&self, rhs: &Self, input: Condition, output: &mut Condition) {
128+
let lo = (*self & u64::MAX as u128) as u64;
129+
let hi = (*self >> 64) as u64;
130+
131+
let mut tmp = 1u8;
132+
lo.cmovne(&((*rhs & u64::MAX as u128) as u64), 0, &mut tmp);
133+
hi.cmovne(&((*rhs >> 64) as u64), 0, &mut tmp);
134+
tmp.cmoveq(&1, input, output);
135+
}
136+
}

cmov/src/portable.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
77
// TODO(tarcieri): more optimized implementation for small integers
88

9-
use crate::{Cmov, Condition};
9+
use crate::{Cmov, CmovEq, Condition};
1010

1111
impl Cmov for u16 {
1212
#[inline(always)]
@@ -24,6 +24,18 @@ impl Cmov for u16 {
2424
}
2525
}
2626

27+
impl CmovEq for u16 {
28+
#[inline(always)]
29+
fn cmovne(&self, rhs: &Self, input: Condition, output: &mut Condition) {
30+
(*self as u64).cmovne(&(*rhs as u64), input, output);
31+
}
32+
33+
#[inline(always)]
34+
fn cmoveq(&self, rhs: &Self, input: Condition, output: &mut Condition) {
35+
(*self as u64).cmoveq(&(*rhs as u64), input, output);
36+
}
37+
}
38+
2739
impl Cmov for u32 {
2840
#[inline(always)]
2941
fn cmovnz(&mut self, value: &Self, condition: Condition) {
@@ -40,6 +52,18 @@ impl Cmov for u32 {
4052
}
4153
}
4254

55+
impl CmovEq for u32 {
56+
#[inline(always)]
57+
fn cmovne(&self, rhs: &Self, input: Condition, output: &mut Condition) {
58+
(*self as u64).cmovne(&(*rhs as u64), input, output);
59+
}
60+
61+
#[inline(always)]
62+
fn cmoveq(&self, rhs: &Self, input: Condition, output: &mut Condition) {
63+
(*self as u64).cmoveq(&(*rhs as u64), input, output);
64+
}
65+
}
66+
4367
impl Cmov for u64 {
4468
#[inline(always)]
4569
fn cmovnz(&mut self, value: &Self, condition: Condition) {
@@ -54,6 +78,18 @@ impl Cmov for u64 {
5478
}
5579
}
5680

81+
impl CmovEq for u64 {
82+
#[inline(always)]
83+
fn cmovne(&self, rhs: &Self, input: Condition, output: &mut Condition) {
84+
output.cmovnz(&input, (self ^ rhs) as u8);
85+
}
86+
87+
#[inline(always)]
88+
fn cmoveq(&self, rhs: &Self, input: Condition, output: &mut Condition) {
89+
output.cmovz(&input, (self ^ rhs) as u8);
90+
}
91+
}
92+
5793
/// Check if the given condition value is non-zero
5894
///
5995
/// # Returns

0 commit comments

Comments
 (0)