Description
I found a bug in Sha512 AVX2 implementation and I'm not sure what's the nicest fix.
to trigger the bug I ran the following code:
use sha2::{Digest, Sha512};
fn main() {
let mut h = Sha512::new();
h.update(&[0u8; 300]);
println!("{:?}", h.finalize());
}
With these flags: RUSTFLAGS='-Clink-arg=-fuse-ld=lld -Clinker-plugin-lto' cargo r
Output:
Segmentation fault (core dumped)
The source of the bug is here: https://github.com/RustCrypto/hashes/blob/c478cbb/sha2/src/sha512/x86.rs#L117
_mm_store_si128
is used with a pointer to a u64 array, which is aligned to 8 and not to 16 as the instruction requires.
(there are a lot of places that assume that MsgSchedule
and RoundStates
are aligned to 16 bytes while this is in fact false)
We need to make them aligned correctly, there are a few ways I see how this can be done:
- Make 2 structs with
repr(align(16))
and implement Index/IndexMut with all theRange*
variants. - Make 2 structs with
repr(align(16))
and transmute their mutable reference to a mutable reference to u64's. - Make a
Align16
type and replace these with arrays of that type, and add a function to receive the lower and higher part of that type.
(we can cut boilerplate of 1 in half via const generics)
Would appreciate thoughts before I implement anything as the first one has a lot of boilerplate code in it.
(P.S. You might notice that &mut ms[2 * $i] as *mut u64 as *mut _
is also a bug because it takes a mutable reference to a single u64 and casts it into a pointer with size 2*64, so reading from that violates Stack Borrows, but that's easier to fix via ms[2 * $i.. 2 * $i + 2].as_mut_ptr() as *mut u64 as *mut _
)