rustpython_common/cmp.rs
1use volatile::Volatile;
2
3/// Compare 2 byte slices in a way that ensures that the timing of the operation can't be used to
4/// glean any information about the data.
5#[inline(never)]
6#[cold]
7pub fn timing_safe_cmp(a: &[u8], b: &[u8]) -> bool {
8 // we use raw pointers here to keep faithful to the C implementation and
9 // to try to avoid any optimizations rustc might do with slices
10 let len_a = a.len();
11 let a = a.as_ptr();
12 let len_b = b.len();
13 let b = b.as_ptr();
14 /* The volatile type declarations make sure that the compiler has no
15 * chance to optimize and fold the code in any way that may change
16 * the timing.
17 */
18 let mut result: u8 = 0;
19 /* loop count depends on length of b */
20 let length: Volatile<usize> = Volatile::new(len_b);
21 let mut left: Volatile<*const u8> = Volatile::new(std::ptr::null());
22 let mut right: Volatile<*const u8> = Volatile::new(b);
23
24 /* don't use else here to keep the amount of CPU instructions constant,
25 * volatile forces re-evaluation
26 * */
27 if len_a == length.read() {
28 left.write(Volatile::new(a).read());
29 result = 0;
30 }
31 if len_a != length.read() {
32 left.write(b);
33 result = 1;
34 }
35
36 for _ in 0..length.read() {
37 let l = left.read();
38 left.write(l.wrapping_add(1));
39 let r = right.read();
40 right.write(r.wrapping_add(1));
41 // safety: the 0..length range will always be either:
42 // * as long as the length of both a and b, if len_a and len_b are equal
43 // * as long as b, and both `left` and `right` are b
44 result |= unsafe { l.read_volatile() ^ r.read_volatile() };
45 }
46
47 result == 0
48}