aboutsummaryrefslogtreecommitdiff
path: root/sys/arm
diff options
context:
space:
mode:
authorMark Johnston <markj@FreeBSD.org>2023-07-27 19:44:00 +0000
committerMark Johnston <markj@FreeBSD.org>2023-08-24 13:33:00 +0000
commitd0434eff57861d0fbff6e31ea541c08979c99428 (patch)
treef440505b3848722f03a22bd6d79d218d7d5a91da /sys/arm
parent045fc75ce490cca0aec8f412dd28bc1031e0919d (diff)
downloadsrc-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.c14
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;
}