checkasm: arm: Check for stack overflows

Figure out the number of stack parameters and make sure that the
value on the stack after those is untouched.

Signed-off-by: Martin Storsjö <martin@martin.st>
This commit is contained in:
Martin Storsjö 2020-05-13 21:09:08 +03:00
parent 3f266cf49e
commit 6cb2d4d94b
2 changed files with 36 additions and 5 deletions

View File

@ -45,6 +45,8 @@ error_message_gpr:
.asciz "failed to preserve register r%d" .asciz "failed to preserve register r%d"
error_message_vfp: error_message_vfp:
.asciz "failed to preserve register d%d" .asciz "failed to preserve register d%d"
error_message_stack:
.asciz "failed to preserve stack"
endconst endconst
@ max number of args used by any asm function. @ max number of args used by any asm function.
@ -52,8 +54,9 @@ endconst
#define ARG_STACK 4*(MAX_ARGS - 4) #define ARG_STACK 4*(MAX_ARGS - 4)
@ align the used stack space to 8 to preserve the stack alignment @ Align the used stack space to 8 to preserve the stack alignment.
#define ARG_STACK_A (((ARG_STACK + pushed + 7) & ~7) - pushed) @ +8 for stack canary reference.
#define ARG_STACK_A (((ARG_STACK + pushed + 7) & ~7) - pushed + 8)
.macro clobbercheck variant .macro clobbercheck variant
.equ pushed, 4*9 .equ pushed, 4*9
@ -80,14 +83,37 @@ function checkasm_checked_call_\variant, export=1
.equ pos, pos + 4 .equ pos, pos + 4
.endr .endr
@ For stack overflows, the callee is free to overwrite the parameters
@ that were passed on the stack (if any), so we can only check after
@ that point. First figure out how many parameters the function
@ really took on the stack:
ldr r12, [sp, #ARG_STACK_A + pushed + 8 + 4*(MAX_ARGS-4)]
@ Load the first non-parameter value from the stack, that should be
@ left untouched by the function. Store a copy of it inverted, so that
@ e.g. overwriting everything with zero would be noticed.
ldr r12, [sp, r12, lsl #2]
mvn r12, r12
str r12, [sp, #ARG_STACK_A - 4]
mov r12, r0 mov r12, r0
mov r0, r2 mov r0, r2
mov r1, r3 mov r1, r3
ldrd r2, r3, [sp, #ARG_STACK_A + pushed] ldrd r2, r3, [sp, #ARG_STACK_A + pushed]
@ Call the target function
blx r12 blx r12
add sp, sp, #ARG_STACK_A
@ Load the number of stack parameters, stack canary and its reference
ldr r12, [sp, #ARG_STACK_A + pushed + 8 + 4*(MAX_ARGS-4)]
ldr r2, [sp, r12, lsl #2]
ldr r3, [sp, #ARG_STACK_A - 4]
add sp, sp, #ARG_STACK_A
push {r0, r1} push {r0, r1}
mvn r3, r3
cmp r2, r3
bne 5f
movrel r12, register_init movrel r12, register_init
.ifc \variant, vfp .ifc \variant, vfp
.macro check_reg_vfp, dreg, offset .macro check_reg_vfp, dreg, offset
@ -141,6 +167,9 @@ function checkasm_checked_call_\variant, export=1
.purgem check_reg .purgem check_reg
b 0f b 0f
5:
movrel r0, error_message_stack
b 1f
4: 4:
movrel r0, error_message_vfp movrel r0, error_message_vfp
b 1f b 1f

View File

@ -177,8 +177,10 @@ void checkasm_stack_clobber(uint64_t clobber, ...);
void checkasm_checked_call_vfp(void *func, int dummy, ...); void checkasm_checked_call_vfp(void *func, int dummy, ...);
void checkasm_checked_call_novfp(void *func, int dummy, ...); void checkasm_checked_call_novfp(void *func, int dummy, ...);
extern void (*checkasm_checked_call)(void *func, int dummy, ...); extern void (*checkasm_checked_call)(void *func, int dummy, ...);
#define declare_new(ret, ...) ret (*checked_call)(void *, int dummy, __VA_ARGS__) = (void *)checkasm_checked_call; #define declare_new(ret, ...) ret (*checked_call)(void *, int dummy, __VA_ARGS__, \
#define call_new(...) checked_call(func_new, 0, __VA_ARGS__) int, int, int, int, int, int, int, int, \
int, int, int, int, int, int, int) = (void *)checkasm_checked_call;
#define call_new(...) checked_call(func_new, 0, __VA_ARGS__, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0)
#elif ARCH_AARCH64 && !defined(__APPLE__) #elif ARCH_AARCH64 && !defined(__APPLE__)
void checkasm_stack_clobber(uint64_t clobber, ...); void checkasm_stack_clobber(uint64_t clobber, ...);
void checkasm_checked_call(void *func, ...); void checkasm_checked_call(void *func, ...);