diff options
author | Mark Johnston <markj@FreeBSD.org> | 2023-07-27 19:44:00 +0000 |
---|---|---|
committer | Mark Johnston <markj@FreeBSD.org> | 2023-08-24 13:33:00 +0000 |
commit | d0434eff57861d0fbff6e31ea541c08979c99428 (patch) | |
tree | f440505b3848722f03a22bd6d79d218d7d5a91da /sys/arm | |
parent | 045fc75ce490cca0aec8f412dd28bc1031e0919d (diff) | |
download | src-d0434eff57861d0fbff6e31ea541c08979c99428.tar.gz src-d0434eff57861d0fbff6e31ea541c08979c99428.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
(cherry picked from commit 1be56e0bb1e8bd8373e446ff9386bcdd764935aa)
Diffstat (limited to 'sys/arm')
-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 4a24d8f13fb1..cdc9ef225ee7 100644 --- a/sys/arm/arm/unwind.c +++ b/sys/arm/arm/unwind.c @@ -33,6 +33,7 @@ #include <sys/kernel.h> #include <sys/linker.h> #include <sys/malloc.h> +#include <sys/proc.h> #include <sys/queue.h> #include <sys/systm.h> @@ -368,6 +369,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; @@ -402,6 +404,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; @@ -428,6 +434,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; @@ -435,6 +444,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++; } @@ -455,6 +466,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; } |