aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Johnston <markj@FreeBSD.org>2023-07-27 19:44:00 +0000
committerMark Johnston <markj@FreeBSD.org>2023-07-27 19:44:00 +0000
commit1be56e0bb1e8bd8373e446ff9386bcdd764935aa (patch)
treec7201638cf8a1f7e6b1b845409cc1bf5398c66fa
parent81a7ce44047f02049760d70c0cdc74c2751e9bad (diff)
downloadsrc-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.c14
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;
}