diff options
author | Mark Johnston <markj@FreeBSD.org> | 2023-07-27 19:44:00 +0000 |
---|---|---|
committer | Mark Johnston <markj@FreeBSD.org> | 2023-07-27 19:44:00 +0000 |
commit | 1be56e0bb1e8bd8373e446ff9386bcdd764935aa (patch) | |
tree | c7201638cf8a1f7e6b1b845409cc1bf5398c66fa | |
parent | 81a7ce44047f02049760d70c0cdc74c2751e9bad (diff) | |
download | src-1be56e0bb1e8bd8373e446ff9386bcdd764935aa.tar.gz src-1be56e0bb1e8bd8373e446ff9386bcdd764935aa.zip |
arm/unwind: Check stack pointer boundaries before dereferencing
If the unwinder somehow ends up with a stack pointer that lies outside
the stack, then an attempt to dereference can lead to a fault, which
causes the kernel to panic again and unwind the stack, which leads to a
fault...
Add kstack_contains() checks at points where we dereference the stack
pointer. This avoids the aforementioned infinite loop in one case I hit
where some OpenSSL assembly code apparently confuses the unwinder.
Reviewed by: jhb
MFC after: 2 weeks
Sponsored by: Klara, Inc.
Sponsored by: Stormshield
Differential Revision: https://reviews.freebsd.org/D41210
-rw-r--r-- | sys/arm/arm/unwind.c | 14 |
1 files changed, 14 insertions, 0 deletions
diff --git a/sys/arm/arm/unwind.c b/sys/arm/arm/unwind.c index bf8ffddfd2c2..5d3309d4539d 100644 --- a/sys/arm/arm/unwind.c +++ b/sys/arm/arm/unwind.c @@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$"); #include <sys/kernel.h> #include <sys/linker.h> #include <sys/malloc.h> +#include <sys/proc.h> #include <sys/queue.h> #include <sys/systm.h> @@ -370,6 +371,7 @@ unwind_exec_read_byte(struct unwind_state *state) static int unwind_exec_insn(struct unwind_state *state) { + struct thread *td = curthread; unsigned int insn; uint32_t *vsp = (uint32_t *)state->registers[SP]; int update_vsp = 0; @@ -404,6 +406,10 @@ unwind_exec_insn(struct unwind_state *state) /* Load the registers */ for (reg = 4; mask && reg < 16; mask >>= 1, reg++) { if (mask & 1) { + if (!kstack_contains(td, (uintptr_t)vsp, + sizeof(*vsp))) + return 1; + state->registers[reg] = *vsp++; state->update_mask |= 1 << reg; @@ -430,6 +436,9 @@ unwind_exec_insn(struct unwind_state *state) update_vsp = 1; /* Pop the registers */ + if (!kstack_contains(td, (uintptr_t)vsp, + sizeof(*vsp) * (4 + count))) + return 1; for (reg = 4; reg <= 4 + count; reg++) { state->registers[reg] = *vsp++; state->update_mask |= 1 << reg; @@ -437,6 +446,8 @@ unwind_exec_insn(struct unwind_state *state) /* Check if we are in the pop r14 version */ if ((insn & INSN_POP_TYPE_MASK) != 0) { + if (!kstack_contains(td, (uintptr_t)vsp, sizeof(*vsp))) + return 1; state->registers[14] = *vsp++; } @@ -457,6 +468,9 @@ unwind_exec_insn(struct unwind_state *state) /* Load the registers */ for (reg = 0; mask && reg < 4; mask >>= 1, reg++) { if (mask & 1) { + if (!kstack_contains(td, (uintptr_t)vsp, + sizeof(*vsp))) + return 1; state->registers[reg] = *vsp++; state->update_mask |= 1 << reg; } |