miri/shims/x86/
sha.rs

1//! Implements sha256 SIMD instructions of x86 targets
2//!
3//! The functions that actually compute SHA256 were copied from [RustCrypto's sha256 module].
4//!
5//! [RustCrypto's sha256 module]: https://github.com/RustCrypto/hashes/blob/6be8466247e936c415d8aafb848697f39894a386/sha2/src/sha256/soft.rs
6
7use rustc_abi::CanonAbi;
8use rustc_middle::ty::Ty;
9use rustc_span::Symbol;
10use rustc_target::callconv::FnAbi;
11
12use crate::*;
13
14impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
15pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
16    fn emulate_x86_sha_intrinsic(
17        &mut self,
18        link_name: Symbol,
19        abi: &FnAbi<'tcx, Ty<'tcx>>,
20        args: &[OpTy<'tcx>],
21        dest: &MPlaceTy<'tcx>,
22    ) -> InterpResult<'tcx, EmulateItemResult> {
23        let this = self.eval_context_mut();
24        this.expect_target_feature_for_intrinsic(link_name, "sha")?;
25        // Prefix should have already been checked.
26        let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.sha").unwrap();
27
28        fn read<'c>(ecx: &mut MiriInterpCx<'c>, reg: &OpTy<'c>) -> InterpResult<'c, [u32; 4]> {
29            let mut res = [0; 4];
30            // We reverse the order because x86 is little endian but the copied implementation uses
31            // big endian.
32            for (i, dst) in res.iter_mut().rev().enumerate() {
33                let projected = &ecx.project_index(reg, i.try_into().unwrap())?;
34                *dst = ecx.read_scalar(projected)?.to_u32()?
35            }
36            interp_ok(res)
37        }
38
39        fn write<'c>(
40            ecx: &mut MiriInterpCx<'c>,
41            dest: &MPlaceTy<'c>,
42            val: [u32; 4],
43        ) -> InterpResult<'c, ()> {
44            // We reverse the order because x86 is little endian but the copied implementation uses
45            // big endian.
46            for (i, part) in val.into_iter().rev().enumerate() {
47                let projected = &ecx.project_index(dest, i.to_u64())?;
48                ecx.write_scalar(Scalar::from_u32(part), projected)?;
49            }
50            interp_ok(())
51        }
52
53        match unprefixed_name {
54            // Used to implement the _mm_sha256rnds2_epu32 function.
55            "256rnds2" => {
56                let [a, b, k] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
57
58                let (a_reg, a_len) = this.project_to_simd(a)?;
59                let (b_reg, b_len) = this.project_to_simd(b)?;
60                let (k_reg, k_len) = this.project_to_simd(k)?;
61                let (dest, dest_len) = this.project_to_simd(dest)?;
62
63                assert_eq!(a_len, 4);
64                assert_eq!(b_len, 4);
65                assert_eq!(k_len, 4);
66                assert_eq!(dest_len, 4);
67
68                let a = read(this, &a_reg)?;
69                let b = read(this, &b_reg)?;
70                let k = read(this, &k_reg)?;
71
72                let result = sha256_digest_round_x2(a, b, k);
73                write(this, &dest, result)?;
74            }
75            // Used to implement the _mm_sha256msg1_epu32 function.
76            "256msg1" => {
77                let [a, b] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
78
79                let (a_reg, a_len) = this.project_to_simd(a)?;
80                let (b_reg, b_len) = this.project_to_simd(b)?;
81                let (dest, dest_len) = this.project_to_simd(dest)?;
82
83                assert_eq!(a_len, 4);
84                assert_eq!(b_len, 4);
85                assert_eq!(dest_len, 4);
86
87                let a = read(this, &a_reg)?;
88                let b = read(this, &b_reg)?;
89
90                let result = sha256msg1(a, b);
91                write(this, &dest, result)?;
92            }
93            // Used to implement the _mm_sha256msg2_epu32 function.
94            "256msg2" => {
95                let [a, b] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
96
97                let (a_reg, a_len) = this.project_to_simd(a)?;
98                let (b_reg, b_len) = this.project_to_simd(b)?;
99                let (dest, dest_len) = this.project_to_simd(dest)?;
100
101                assert_eq!(a_len, 4);
102                assert_eq!(b_len, 4);
103                assert_eq!(dest_len, 4);
104
105                let a = read(this, &a_reg)?;
106                let b = read(this, &b_reg)?;
107
108                let result = sha256msg2(a, b);
109                write(this, &dest, result)?;
110            }
111            _ => return interp_ok(EmulateItemResult::NotSupported),
112        }
113        interp_ok(EmulateItemResult::NeedsReturn)
114    }
115}
116
117#[inline(always)]
118fn shr(v: [u32; 4], o: u32) -> [u32; 4] {
119    [v[0] >> o, v[1] >> o, v[2] >> o, v[3] >> o]
120}
121
122#[inline(always)]
123fn shl(v: [u32; 4], o: u32) -> [u32; 4] {
124    [v[0] << o, v[1] << o, v[2] << o, v[3] << o]
125}
126
127#[inline(always)]
128fn or(a: [u32; 4], b: [u32; 4]) -> [u32; 4] {
129    [a[0] | b[0], a[1] | b[1], a[2] | b[2], a[3] | b[3]]
130}
131
132#[inline(always)]
133fn xor(a: [u32; 4], b: [u32; 4]) -> [u32; 4] {
134    [a[0] ^ b[0], a[1] ^ b[1], a[2] ^ b[2], a[3] ^ b[3]]
135}
136
137#[inline(always)]
138fn add(a: [u32; 4], b: [u32; 4]) -> [u32; 4] {
139    [
140        a[0].wrapping_add(b[0]),
141        a[1].wrapping_add(b[1]),
142        a[2].wrapping_add(b[2]),
143        a[3].wrapping_add(b[3]),
144    ]
145}
146
147fn sha256load(v2: [u32; 4], v3: [u32; 4]) -> [u32; 4] {
148    [v3[3], v2[0], v2[1], v2[2]]
149}
150
151fn sha256_digest_round_x2(cdgh: [u32; 4], abef: [u32; 4], wk: [u32; 4]) -> [u32; 4] {
152    macro_rules! big_sigma0 {
153        ($a:expr) => {
154            ($a.rotate_right(2) ^ $a.rotate_right(13) ^ $a.rotate_right(22))
155        };
156    }
157    macro_rules! big_sigma1 {
158        ($a:expr) => {
159            ($a.rotate_right(6) ^ $a.rotate_right(11) ^ $a.rotate_right(25))
160        };
161    }
162    macro_rules! bool3ary_202 {
163        ($a:expr, $b:expr, $c:expr) => {
164            $c ^ ($a & ($b ^ $c))
165        };
166    } // Choose, MD5F, SHA1C
167    macro_rules! bool3ary_232 {
168        ($a:expr, $b:expr, $c:expr) => {
169            ($a & $b) ^ ($a & $c) ^ ($b & $c)
170        };
171    } // Majority, SHA1M
172
173    let [_, _, wk1, wk0] = wk;
174    let [a0, b0, e0, f0] = abef;
175    let [c0, d0, g0, h0] = cdgh;
176
177    // a round
178    let x0 =
179        big_sigma1!(e0).wrapping_add(bool3ary_202!(e0, f0, g0)).wrapping_add(wk0).wrapping_add(h0);
180    let y0 = big_sigma0!(a0).wrapping_add(bool3ary_232!(a0, b0, c0));
181    let (a1, b1, c1, d1, e1, f1, g1, h1) =
182        (x0.wrapping_add(y0), a0, b0, c0, x0.wrapping_add(d0), e0, f0, g0);
183
184    // a round
185    let x1 =
186        big_sigma1!(e1).wrapping_add(bool3ary_202!(e1, f1, g1)).wrapping_add(wk1).wrapping_add(h1);
187    let y1 = big_sigma0!(a1).wrapping_add(bool3ary_232!(a1, b1, c1));
188    let (a2, b2, _, _, e2, f2, _, _) =
189        (x1.wrapping_add(y1), a1, b1, c1, x1.wrapping_add(d1), e1, f1, g1);
190
191    [a2, b2, e2, f2]
192}
193
194fn sha256msg1(v0: [u32; 4], v1: [u32; 4]) -> [u32; 4] {
195    // sigma 0 on vectors
196    #[inline]
197    fn sigma0x4(x: [u32; 4]) -> [u32; 4] {
198        let t1 = or(shr(x, 7), shl(x, 25));
199        let t2 = or(shr(x, 18), shl(x, 14));
200        let t3 = shr(x, 3);
201        xor(xor(t1, t2), t3)
202    }
203
204    add(v0, sigma0x4(sha256load(v0, v1)))
205}
206
207fn sha256msg2(v4: [u32; 4], v3: [u32; 4]) -> [u32; 4] {
208    macro_rules! sigma1 {
209        ($a:expr) => {
210            $a.rotate_right(17) ^ $a.rotate_right(19) ^ ($a >> 10)
211        };
212    }
213
214    let [x3, x2, x1, x0] = v4;
215    let [w15, w14, _, _] = v3;
216
217    let w16 = x0.wrapping_add(sigma1!(w14));
218    let w17 = x1.wrapping_add(sigma1!(w15));
219    let w18 = x2.wrapping_add(sigma1!(w16));
220    let w19 = x3.wrapping_add(sigma1!(w17));
221
222    [w19, w18, w17, w16]
223}