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
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user