How could the movdqa/movdqu instruction write an incorrect value to memory?

517 views Asked by At

I'm trying to implement AES encryption using the Intel AES Instruction Set, and I've encountered a strange issue when moving the contents of an xmm register to memory. I'm trying to move the contents of %xmm1 to the stack address in %rsi. Here's my code:

movdqa (%rdi), %xmm1
movdqa %xmm1, (%rsi)

The contents of (%rdi) are moved to xmm1 as expected, but when moving them to (%rsi), the first dword is zeroed and the last dword is not copied:

(lldb) mem read $rdi -c 0x10
0x7ffeefbff3a0: 30 28 00 00 00 00 00 00 42 42 42 42 41 41 41 41  0(......BBBBAAAA
(lldb) mem read $rsi -c 0x10
0x7ffeefbff2f0: 00 00 00 00 00 00 00 00 20 71 4e 00 00 00 00 00  ........ qN.....
(lldb) s
(lldb) reg read xmm1
    xmm1 = {0x30 0x28 0x00 0x00 0x00 0x00 0x00 0x00 0x42 0x42 0x42 0x42 0x41 0x41 0x41 0x41}
(lldb) s
(lldb) mem read $rsi -c 0x10
0x7ffeefbff2f0: 00 00 00 00 00 00 00 00 42 42 42 42 00 00 00 00  ........BBBB....

Even though the memory is 16 byte-aligned, I also tried the movdqu instruction and got the same result. Does anyone know the reason for this behavior, or have ideas for workarounds?

Here's my code, the problem occurs at line 3 in copy_aes_key_schedule. I'm compiling and debugging it with Xcode.

In main.c:

#include <stdio.h>

extern void encrypt_function_call(uint64_t functionID, uint64_t returnAddress, uint64_t opcodes, uint8_t result[0x10]);

int main(int argc, const char * argv[]) {
    // insert code here...
    
    uint32_t encrypted[4];
    encrypt_function_call(0xd67f1567, 0x2830, 0x4141414142424242, encrypted);
    
    return 0;
}

In e.s:

.globl _encrypt_function_call

key_expansion_128:
pshufd $0xff, %xmm2, %xmm2
vpslldq $4, %xmm1, %xmm3
pxor %xmm3, %xmm1
vpslldq $4, %xmm1, %xmm3
pxor %xmm3, %xmm1
vpslldq $4, %xmm1, %xmm3
pxor %xmm3, %xmm1
pxor %xmm2, %xmm1
movdqu %xmm1, (%rcx)
add $0x10, %rcx
ret

copy_aes_key_schedule:
push %rcx
movdqa (%rdi), %xmm1
movdqa %xmm1, (%rsi)
lea 0x10(%rsi), %rcx // Untested from here on
aeskeygenassist $1, %xmm1, %xmm2
call key_expansion_128
aeskeygenassist $2, %xmm1, %xmm2
call key_expansion_128
aeskeygenassist $4, %xmm1, %xmm2
call key_expansion_128
aeskeygenassist $8, %xmm1, %xmm2
call key_expansion_128
aeskeygenassist $0x10, %xmm1, %xmm2
call key_expansion_128
aeskeygenassist $0x20, %xmm1, %xmm2
call key_expansion_128
aeskeygenassist $0x40, %xmm1, %xmm2
call key_expansion_128
aeskeygenassist $0x80, %xmm1, %xmm2
call key_expansion_128
aeskeygenassist $0x1b, %xmm1, %xmm2
call key_expansion_128
aeskeygenassist $0x36, %xmm1, %xmm2
call key_expansion_128
pop %rcx
ret

_encrypt_function_call:
push %rbp
mov %rsp, %rbp
push %r12
push %r13
push %r14
push %r15
mov %rcx, %r15
lea -0x30(%rbp), %r10 // data
mov %rdi, (%r10)
rdrand %r12
mov %r12, 8(%r10)
rdrand %r12d
mov %r12d, 4(%r10)
lea -0x40(%rbp), %rdi // key
mov %rsi, (%rdi)
mov %rdx, 8(%rdi)
lea -0xf0(%rbp), %rsi
call copy_aes_key_schedule
call _abort // untested from here on
movdqu (%r10), %xmm15
movdqu (%rsi), %xmm0
pxor %xmm0, %xmm15
movdqu 0x10(%rsi), %xmm0
aesenc %xmm0, %xmm15
movdqu 0x20(%rsi), %xmm0
aesenc %xmm0, %xmm15
movdqu 0x30(%rsi), %xmm0
aesenc %xmm0, %xmm15
movdqu 0x40(%rsi), %xmm0
aesenc %xmm0, %xmm15
movdqu 0x50(%rsi), %xmm0
aesenc %xmm0, %xmm15
movdqu 0x60(%rsi), %xmm0
aesenc %xmm0, %xmm15
movdqu 0x70(%rsi), %xmm0
aesenc %xmm0, %xmm15
movdqu 0x80(%rsi), %xmm0
aesenc %xmm0, %xmm15
movdqu 0x90(%rsi), %xmm0
aesenc %xmm0, %xmm15
movdqu 0xa0(%rsi), %xmm0
aesenclast %xmm0, %xmm15
movdqu %xmm15, (%r15)
pop %r15
pop %r14
pop %r13
pop %r12
pop %rbp
ret
1

There are 1 answers

0
jk9357 On

After further investigation and more strange behavior, I solved the problem by increasing the size of the stack variable which holds the AES key schedule result to 0xf0 bytes, even though the actual key schedule is only 0xb0 bytes.