Skip to content

Commit 12bdf05

Browse files
KAGA-KOKOlizf-os
authored andcommitted
x86/process: Add proper bound checks in 64bit get_wchan()
commit eddd3826a1a0190e5235703d1e666affa4d13b96 upstream. Dmitry Vyukov reported the following using trinity and the memory error detector AddressSanitizer (https://code.google.com/p/address-sanitizer/wiki/AddressSanitizerForKernel). [ 124.575597] ERROR: AddressSanitizer: heap-buffer-overflow on address ffff88002e280000 [ 124.576801] ffff88002e280000 is located 131938492886538 bytes to the left of 28857600-byte region [ffffffff81282e0a, ffffffff82e0830a) [ 124.578633] Accessed by thread T10915: [ 124.579295] inlined in describe_heap_address ./arch/x86/mm/asan/report.c:164 [ 124.579295] #0 ffffffff810dd277 in asan_report_error ./arch/x86/mm/asan/report.c:278 [ 124.580137] #1 ffffffff810dc6a0 in asan_check_region ./arch/x86/mm/asan/asan.c:37 [ 124.581050] #2 ffffffff810dd423 in __tsan_read8 ??:0 [ 124.581893] Quarx2k#3 ffffffff8107c093 in get_wchan ./arch/x86/kernel/process_64.c:444 The address checks in the 64bit implementation of get_wchan() are wrong in several ways: - The lower bound of the stack is not the start of the stack page. It's the start of the stack page plus sizeof (struct thread_info) - The upper bound must be: top_of_stack - TOP_OF_KERNEL_STACK_PADDING - 2 * sizeof(unsigned long). The 2 * sizeof(unsigned long) is required because the stack pointer points at the frame pointer. The layout on the stack is: ... IP FP ... IP FP. So we need to make sure that both IP and FP are in the bounds. Fix the bound checks and get rid of the mix of numeric constants, u64 and unsigned long. Making all unsigned long allows us to use the same function for 32bit as well. Use READ_ONCE() when accessing the stack. This does not prevent a concurrent wakeup of the task and the stack changing, but at least it avoids TOCTOU. Also check task state at the end of the loop. Again that does not prevent concurrent changes, but it avoids walking for nothing. Add proper comments while at it. Reported-by: Dmitry Vyukov <[email protected]> Reported-by: Sasha Levin <[email protected]> Based-on-patch-from: Wolfram Gloger <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Reviewed-by: Borislav Petkov <[email protected]> Reviewed-by: Dmitry Vyukov <[email protected]> Cc: Andrey Ryabinin <[email protected]> Cc: Andy Lutomirski <[email protected]> Cc: Andrey Konovalov <[email protected]> Cc: Kostya Serebryany <[email protected]> Cc: Alexander Potapenko <[email protected]> Cc: kasan-dev <[email protected]> Cc: Denys Vlasenko <[email protected]> Cc: Andi Kleen <[email protected]> Cc: Wolfram Gloger <[email protected]> Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Thomas Gleixner <[email protected]> [lizf: Backported to 3.4: - s/READ_ONCE/ACCESS_ONCE - remove TOP_OF_KERNEL_STACK_PADDING] Signed-off-by: Zefan Li <[email protected]>
1 parent 127cf7c commit 12bdf05

File tree

1 file changed

+42
-10
lines changed

1 file changed

+42
-10
lines changed

arch/x86/kernel/process_64.c

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -470,27 +470,59 @@ void set_personality_ia32(bool x32)
470470
}
471471
EXPORT_SYMBOL_GPL(set_personality_ia32);
472472

473+
/*
474+
* Called from fs/proc with a reference on @p to find the function
475+
* which called into schedule(). This needs to be done carefully
476+
* because the task might wake up and we might look at a stack
477+
* changing under us.
478+
*/
473479
unsigned long get_wchan(struct task_struct *p)
474480
{
475-
unsigned long stack;
476-
u64 fp, ip;
481+
unsigned long start, bottom, top, sp, fp, ip;
477482
int count = 0;
478483

479484
if (!p || p == current || p->state == TASK_RUNNING)
480485
return 0;
481-
stack = (unsigned long)task_stack_page(p);
482-
if (p->thread.sp < stack || p->thread.sp >= stack+THREAD_SIZE)
486+
487+
start = (unsigned long)task_stack_page(p);
488+
if (!start)
489+
return 0;
490+
491+
/*
492+
* Layout of the stack page:
493+
*
494+
* ----------- topmax = start + THREAD_SIZE - sizeof(unsigned long)
495+
* PADDING
496+
* ----------- top = topmax - TOP_OF_KERNEL_STACK_PADDING
497+
* stack
498+
* ----------- bottom = start + sizeof(thread_info)
499+
* thread_info
500+
* ----------- start
501+
*
502+
* The tasks stack pointer points at the location where the
503+
* framepointer is stored. The data on the stack is:
504+
* ... IP FP ... IP FP
505+
*
506+
* We need to read FP and IP, so we need to adjust the upper
507+
* bound by another unsigned long.
508+
*/
509+
top = start + THREAD_SIZE;
510+
top -= 2 * sizeof(unsigned long);
511+
bottom = start + sizeof(struct thread_info);
512+
513+
sp = ACCESS_ONCE(p->thread.sp);
514+
if (sp < bottom || sp > top)
483515
return 0;
484-
fp = *(u64 *)(p->thread.sp);
516+
517+
fp = ACCESS_ONCE(*(unsigned long *)sp);
485518
do {
486-
if (fp < (unsigned long)stack ||
487-
fp >= (unsigned long)stack+THREAD_SIZE)
519+
if (fp < bottom || fp > top)
488520
return 0;
489-
ip = *(u64 *)(fp+8);
521+
ip = ACCESS_ONCE(*(unsigned long *)(fp + sizeof(unsigned long)));
490522
if (!in_sched_functions(ip))
491523
return ip;
492-
fp = *(u64 *)fp;
493-
} while (count++ < 16);
524+
fp = ACCESS_ONCE(*(unsigned long *)fp);
525+
} while (count++ < 16 && p->state != TASK_RUNNING);
494526
return 0;
495527
}
496528

0 commit comments

Comments
 (0)