miri/shims/x86/
aesni.rs

1use rustc_abi::CanonAbi;
2use rustc_middle::ty::Ty;
3use rustc_span::Symbol;
4use rustc_target::callconv::FnAbi;
5
6use crate::*;
7
8impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
9pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
10    fn emulate_x86_aesni_intrinsic(
11        &mut self,
12        link_name: Symbol,
13        abi: &FnAbi<'tcx, Ty<'tcx>>,
14        args: &[OpTy<'tcx>],
15        dest: &MPlaceTy<'tcx>,
16    ) -> InterpResult<'tcx, EmulateItemResult> {
17        let this = self.eval_context_mut();
18        this.expect_target_feature_for_intrinsic(link_name, "aes")?;
19        // Prefix should have already been checked.
20        let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.aesni.").unwrap();
21
22        match unprefixed_name {
23            // Used to implement the _mm_aesdec_si128, _mm256_aesdec_epi128
24            // and _mm512_aesdec_epi128 functions.
25            // Performs one round of an AES decryption on each 128-bit word of
26            // `state` with the corresponding 128-bit key of `key`.
27            // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdec_si128
28            "aesdec" | "aesdec.256" | "aesdec.512" => {
29                let [state, key] =
30                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
31                aes_round(this, state, key, dest, |state, key| {
32                    let key = aes::Block::from(key.to_le_bytes());
33                    let mut state = aes::Block::from(state.to_le_bytes());
34                    // `aes::hazmat::equiv_inv_cipher_round` documentation states that
35                    // it performs the same operation as the x86 aesdec instruction.
36                    aes::hazmat::equiv_inv_cipher_round(&mut state, &key);
37                    u128::from_le_bytes(state.into())
38                })?;
39            }
40            // Used to implement the _mm_aesdeclast_si128, _mm256_aesdeclast_epi128
41            // and _mm512_aesdeclast_epi128 functions.
42            // Performs last round of an AES decryption on each 128-bit word of
43            // `state` with the corresponding 128-bit key of `key`.
44            // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdeclast_si128
45            "aesdeclast" | "aesdeclast.256" | "aesdeclast.512" => {
46                let [state, key] =
47                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
48
49                aes_round(this, state, key, dest, |state, key| {
50                    let mut state = aes::Block::from(state.to_le_bytes());
51                    // `aes::hazmat::equiv_inv_cipher_round` does the following operations:
52                    // state = InvShiftRows(state)
53                    // state = InvSubBytes(state)
54                    // state = InvMixColumns(state)
55                    // state = state ^ key
56                    // But we need to skip the InvMixColumns.
57                    // First, use a zeroed key to skip the XOR.
58                    aes::hazmat::equiv_inv_cipher_round(&mut state, &aes::Block::from([0; 16]));
59                    // Then, undo the InvMixColumns with MixColumns.
60                    aes::hazmat::mix_columns(&mut state);
61                    // Finally, do the XOR.
62                    u128::from_le_bytes(state.into()) ^ key
63                })?;
64            }
65            // Used to implement the _mm_aesenc_si128, _mm256_aesenc_epi128
66            // and _mm512_aesenc_epi128 functions.
67            // Performs one round of an AES encryption on each 128-bit word of
68            // `state` with the corresponding 128-bit key of `key`.
69            // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesenc_si128
70            "aesenc" | "aesenc.256" | "aesenc.512" => {
71                let [state, key] =
72                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
73                aes_round(this, state, key, dest, |state, key| {
74                    let key = aes::Block::from(key.to_le_bytes());
75                    let mut state = aes::Block::from(state.to_le_bytes());
76                    // `aes::hazmat::cipher_round` documentation states that
77                    // it performs the same operation as the x86 aesenc instruction.
78                    aes::hazmat::cipher_round(&mut state, &key);
79                    u128::from_le_bytes(state.into())
80                })?;
81            }
82            // Used to implement the _mm_aesenclast_si128, _mm256_aesenclast_epi128
83            // and _mm512_aesenclast_epi128 functions.
84            // Performs last round of an AES encryption on each 128-bit word of
85            // `state` with the corresponding 128-bit key of `key`.
86            // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesenclast_si128
87            "aesenclast" | "aesenclast.256" | "aesenclast.512" => {
88                let [state, key] =
89                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
90                aes_round(this, state, key, dest, |state, key| {
91                    let mut state = aes::Block::from(state.to_le_bytes());
92                    // `aes::hazmat::cipher_round` does the following operations:
93                    // state = ShiftRows(state)
94                    // state = SubBytes(state)
95                    // state = MixColumns(state)
96                    // state = state ^ key
97                    // But we need to skip the MixColumns.
98                    // First, use a zeroed key to skip the XOR.
99                    aes::hazmat::cipher_round(&mut state, &aes::Block::from([0; 16]));
100                    // Then, undo the MixColumns with InvMixColumns.
101                    aes::hazmat::inv_mix_columns(&mut state);
102                    // Finally, do the XOR.
103                    u128::from_le_bytes(state.into()) ^ key
104                })?;
105            }
106            // Used to implement the _mm_aesimc_si128 function.
107            // Performs the AES InvMixColumns operation on `op`
108            "aesimc" => {
109                let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
110                // Transmute to `u128`
111                let op = op.transmute(this.machine.layouts.u128, this)?;
112                let dest = dest.transmute(this.machine.layouts.u128, this)?;
113
114                let state = this.read_scalar(&op)?.to_u128()?;
115                let mut state = aes::Block::from(state.to_le_bytes());
116                aes::hazmat::inv_mix_columns(&mut state);
117
118                this.write_scalar(Scalar::from_u128(u128::from_le_bytes(state.into())), &dest)?;
119            }
120            // TODO: Implement the `llvm.x86.aesni.aeskeygenassist` when possible
121            // with an external crate.
122            _ => return interp_ok(EmulateItemResult::NotSupported),
123        }
124        interp_ok(EmulateItemResult::NeedsReturn)
125    }
126}
127
128// Performs an AES round (given by `f`) on each 128-bit word of
129// `state` with the corresponding 128-bit key of `key`.
130fn aes_round<'tcx>(
131    ecx: &mut crate::MiriInterpCx<'tcx>,
132    state: &OpTy<'tcx>,
133    key: &OpTy<'tcx>,
134    dest: &MPlaceTy<'tcx>,
135    f: impl Fn(u128, u128) -> u128,
136) -> InterpResult<'tcx, ()> {
137    assert_eq!(dest.layout.size, state.layout.size);
138    assert_eq!(dest.layout.size, key.layout.size);
139
140    // Transmute arguments to arrays of `u128`.
141    assert_eq!(dest.layout.size.bytes() % 16, 0);
142    let len = dest.layout.size.bytes() / 16;
143
144    let u128_array_layout = ecx.layout_of(Ty::new_array(ecx.tcx.tcx, ecx.tcx.types.u128, len))?;
145
146    let state = state.transmute(u128_array_layout, ecx)?;
147    let key = key.transmute(u128_array_layout, ecx)?;
148    let dest = dest.transmute(u128_array_layout, ecx)?;
149
150    for i in 0..len {
151        let state = ecx.read_scalar(&ecx.project_index(&state, i)?)?.to_u128()?;
152        let key = ecx.read_scalar(&ecx.project_index(&key, i)?)?.to_u128()?;
153        let dest = ecx.project_index(&dest, i)?;
154
155        let res = f(state, key);
156
157        ecx.write_scalar(Scalar::from_u128(res), &dest)?;
158    }
159
160    interp_ok(())
161}