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}