diff options
author | Andy Lutomirski <luto@kernel.org> | 2016-09-16 01:45:46 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2016-09-16 03:18:53 -0400 |
commit | 74327a3e884a0ff895ba7b51d3488e6a177407b2 (patch) | |
tree | 9efa0dedd51c86d132d29b869a119e69b7ab76cb /arch/x86/kernel/process.c | |
parent | 1959a60182f48879635812a03a99c02231ea8677 (diff) |
x86/process: Pin the target stack in get_wchan()
This will prevent a crash if get_wchan() runs after the task stack
is freed.
Signed-off-by: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Jann Horn <jann@thejh.net>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/337aeca8614024aa4d8d9c81053bbf8fcffbe4ad.1474003868.git.luto@kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'arch/x86/kernel/process.c')
-rw-r--r-- | arch/x86/kernel/process.c | 22 |
1 files changed, 15 insertions, 7 deletions
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index 0b9ed8ec5226..4002b475171c 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c | |||
@@ -532,15 +532,18 @@ unsigned long thread_saved_pc(struct task_struct *tsk) | |||
532 | */ | 532 | */ |
533 | unsigned long get_wchan(struct task_struct *p) | 533 | unsigned long get_wchan(struct task_struct *p) |
534 | { | 534 | { |
535 | unsigned long start, bottom, top, sp, fp, ip; | 535 | unsigned long start, bottom, top, sp, fp, ip, ret = 0; |
536 | int count = 0; | 536 | int count = 0; |
537 | 537 | ||
538 | if (!p || p == current || p->state == TASK_RUNNING) | 538 | if (!p || p == current || p->state == TASK_RUNNING) |
539 | return 0; | 539 | return 0; |
540 | 540 | ||
541 | if (!try_get_task_stack(p)) | ||
542 | return 0; | ||
543 | |||
541 | start = (unsigned long)task_stack_page(p); | 544 | start = (unsigned long)task_stack_page(p); |
542 | if (!start) | 545 | if (!start) |
543 | return 0; | 546 | goto out; |
544 | 547 | ||
545 | /* | 548 | /* |
546 | * Layout of the stack page: | 549 | * Layout of the stack page: |
@@ -564,16 +567,21 @@ unsigned long get_wchan(struct task_struct *p) | |||
564 | 567 | ||
565 | sp = READ_ONCE(p->thread.sp); | 568 | sp = READ_ONCE(p->thread.sp); |
566 | if (sp < bottom || sp > top) | 569 | if (sp < bottom || sp > top) |
567 | return 0; | 570 | goto out; |
568 | 571 | ||
569 | fp = READ_ONCE_NOCHECK(((struct inactive_task_frame *)sp)->bp); | 572 | fp = READ_ONCE_NOCHECK(((struct inactive_task_frame *)sp)->bp); |
570 | do { | 573 | do { |
571 | if (fp < bottom || fp > top) | 574 | if (fp < bottom || fp > top) |
572 | return 0; | 575 | goto out; |
573 | ip = READ_ONCE_NOCHECK(*(unsigned long *)(fp + sizeof(unsigned long))); | 576 | ip = READ_ONCE_NOCHECK(*(unsigned long *)(fp + sizeof(unsigned long))); |
574 | if (!in_sched_functions(ip)) | 577 | if (!in_sched_functions(ip)) { |
575 | return ip; | 578 | ret = ip; |
579 | goto out; | ||
580 | } | ||
576 | fp = READ_ONCE_NOCHECK(*(unsigned long *)fp); | 581 | fp = READ_ONCE_NOCHECK(*(unsigned long *)fp); |
577 | } while (count++ < 16 && p->state != TASK_RUNNING); | 582 | } while (count++ < 16 && p->state != TASK_RUNNING); |
578 | return 0; | 583 | |
584 | out: | ||
585 | put_task_stack(p); | ||
586 | return ret; | ||
579 | } | 587 | } |