Add a kstack_contains() helper function.

This is useful for stack unwinders which need to avoid out-of-bounds
reads of a kernel stack which can trigger kernel faults.

Reviewed by:	kib, markj
Obtained from:	CheriBSD
Sponsored by:	DARPA
Differential Revision:	https://reviews.freebsd.org/D27356
This commit is contained in:
John Baldwin 2020-12-01 17:04:46 +00:00
parent c49747eaf2
commit 5941edfcdc
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=368240
6 changed files with 21 additions and 30 deletions

View file

@ -73,12 +73,8 @@ dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,
frame = (struct amd64_frame *)rbp;
td = curthread;
while (depth < pcstack_limit) {
if (!INKERNEL((long) frame))
break;
if ((vm_offset_t)frame >=
td->td_kstack + ptoa(td->td_kstack_pages) ||
(vm_offset_t)frame < td->td_kstack)
if (!kstack_contains(curthread, (vm_offset_t)frame,
sizeof(*frame))
break;
callpc = frame->f_retaddr;
@ -466,14 +462,11 @@ dtrace_getstackdepth(int aframes)
frame = (struct amd64_frame *)rbp;
depth++;
for(;;) {
if (!INKERNEL((long) frame))
break;
if (!INKERNEL((long) frame->f_frame))
if (!kstack_contains(curthread, (vm_offset_t)frame,
sizeof(*frame))
break;
depth++;
if (frame->f_frame <= frame ||
(vm_offset_t)frame->f_frame >= curthread->td_kstack +
curthread->td_kstack_pages * PAGE_SIZE)
if (frame->f_frame <= frame)
break;
frame = frame->f_frame;
}

View file

@ -73,7 +73,8 @@ dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,
frame = (struct i386_frame *)ebp;
while (depth < pcstack_limit) {
if (!INKERNEL(frame))
if (!kstack_contains(curthread, (vm_offset_t)frame,
sizeof(*frame))
break;
callpc = frame->f_retaddr;
@ -91,9 +92,7 @@ dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,
pcstack[depth++] = callpc;
}
if (frame->f_frame <= frame ||
(vm_offset_t)frame->f_frame >= curthread->td_kstack +
curthread->td_kstack_pages * PAGE_SIZE)
if (frame->f_frame <= frame)
break;
frame = frame->f_frame;
}
@ -484,14 +483,10 @@ dtrace_getstackdepth(int aframes)
frame = (struct i386_frame *)ebp;
depth++;
for(;;) {
if (!INKERNEL((long) frame))
break;
if (!INKERNEL((long) frame->f_frame))
if (!kstack_contains((vm_offset_t)frame, sizeof(*frame))
break;
depth++;
if (frame->f_frame <= frame ||
(vm_offset_t)frame->f_frame >= curthread->td_kstack +
curthread->td_kstack_pages * PAGE_SIZE)
if (frame->f_frame <= frame)
break;
frame = frame->f_frame;
}

View file

@ -527,8 +527,7 @@ db_findstack_cmd(db_expr_t addr, bool have_addr, db_expr_t dummy3 __unused,
FOREACH_PROC_IN_SYSTEM(p) {
FOREACH_THREAD_IN_PROC(p, td) {
if (td->td_kstack <= saddr && saddr < td->td_kstack +
PAGE_SIZE * td->td_kstack_pages) {
if (kstack_contains(td, saddr, 1)) {
db_printf("Thread %p\n", td);
return;
}

View file

@ -53,9 +53,8 @@ stack_capture(struct thread *td, struct stack *st, struct unwind_state *frame)
stack_zero(st);
while (1) {
if ((vm_offset_t)frame->fp < td->td_kstack ||
(vm_offset_t)frame->fp >= td->td_kstack +
td->td_kstack_pages * PAGE_SIZE)
if (!kstack_contains(td, (vm_offset_t)frame->fp -
(sizeof(uintptr_t) * 2), sizeof(uintptr_t) * 2))
break;
unwind_frame(frame);
if (!INKERNEL((vm_offset_t)frame->pc))

View file

@ -1198,6 +1198,13 @@ curthread_pflags2_restore(int save)
curthread->td_pflags2 &= save;
}
static __inline bool
kstack_contains(struct thread *td, vm_offset_t va, size_t len)
{
return (va >= td->td_kstack && va + len >= va &&
va + len <= td->td_kstack + td->td_kstack_pages * PAGE_SIZE);
}
static __inline __pure2 struct td_sched *
td_get_sched(struct thread *td)
{

View file

@ -79,9 +79,7 @@ stack_capture(struct thread *td, struct stack *st, register_t fp)
stack_zero(st);
frame = (x86_frame_t)fp;
while (1) {
if ((vm_offset_t)frame < td->td_kstack ||
(vm_offset_t)frame >= td->td_kstack +
td->td_kstack_pages * PAGE_SIZE)
if (!kstack_contains(td, (vm_offset_t)frame, sizeof(*frame)))
break;
callpc = frame->f_retaddr;
if (!INKERNEL(callpc))