utils ct: replace black_box with a volatile optimization barrier in ct_eq_bytes and friends - #131
utils ct: replace black_box with a volatile optimization barrier in ct_eq_bytes and friends#131ounsworth wants to merge 1 commit into
Conversation
…t_eq_bytes and friends Survey of other libraries (from their current upstream sources): libsodium's `sodium_memcmp` uses a `volatile` accumulator and volatile input pointers, and subtle's `Choice::from` is an `#[inline(never)]` `read_volatile`, so both place the barrier inside the loop per byte. `constant_time_eq` and RustCrypto's `cmov` chunk into machine words and apply an inline-asm barrier (or `cmov`/`csel`) per word, which is the shape adopted here. Graviola writes the whole byte loop in inline asm on x86_64 and aarch64. OpenSSL's `CRYPTO_memcmp` reads through volatile pointers only, and BoringSSL's uses no barrier at all. Assembly was inspected by Claude from release builds on x86_64, i686, thumbv7em (Cortex-M4), riscv32imac, wasm32, msp430 (16-bit) and avr-none (8-bit): in every case the loop is load, xor, or, one native-width store to a stack slot and one reload, with a byte-wise tail, no `bcmp`/`memcmp` call and no data-dependent branch, and the final `== 0` tests the volatile-loaded value. Integration tests now sweep every length from 0 to 40 with a single bit flipped at every position, covering the word and tail paths on both sides of every boundary for 2, 4 and 8-byte words. Co-Authored-By: Claude Fable 5
d3ebc7c to
e89d8fd
Compare
|
Analysis courtesy of Opus 5. Some of these errors are extremely odd if this PR was generated using Fable - the LLMs do lend more weight to comments/text than code by default which could explain the mutant issue, but I'm not sure about the rest. 1. A killable mutant survives:
|
Survey of other libraries (from their current upstream sources): libsodium's
sodium_memcmpuses avolatileaccumulator and volatile input pointers, and subtle'sChoice::fromis an#[inline(never)]read_volatile, so both place the barrier inside the loop per byte.constant_time_eqand RustCrypto'scmovchunk into machine words and apply an inline-asm barrier (orcmov/csel) per word, which is the shape adopted here. Graviola writes the whole byte loop in inline asm on x86_64 and aarch64. OpenSSL'sCRYPTO_memcmpreads through volatile pointers only, and BoringSSL's uses no barrier at all.Assembly was inspected by Claude from release builds on x86_64, i686, thumbv7em (Cortex-M4), riscv32imac, wasm32, msp430 (16-bit) and avr-none (8-bit): in every case the loop is load, xor, or, one native-width store to a stack slot and one reload, with a byte-wise tail, no
bcmp/memcmpcall and no data-dependent branch, and the final== 0tests the volatile-loaded value.Integration tests now sweep every length from 0 to 40 with a single bit flipped at every position, covering the word and tail paths on both sides of every boundary for 2, 4 and 8-byte words.
Co-Authored-By: Claude Fable 5
Closes #128