From: Thomas Gleixner <tglx@linutronix.de>
To: LKML <linux-kernel@vger.kernel.org>
Cc: Andrey Ryabinin <ryabinin.a.a@gmail.com>,
Andy Lutomirski <luto@amacapital.net>,
Andrey Konovalov <andreyknvl@google.com>,
Kostya Serebryany <kcc@google.com>,
Alexander Potapenko <glider@google.com>,
kasan-dev <kasan-dev@googlegroups.com>,
Borislav Petkov <bp@alien8.de>,
Denys Vlasenko <dvlasenk@redhat.com>,
Andi Kleen <ak@linux.intel.com>,
x86@kernel.org, Dmitry Vyukov <dvyukov@google.com>,
Sasha Levin <sasha.levin@oracle.com>,
Wolfram Gloger <wmglo@dent.med.uni-muenchen.de>
Subject: [patch 1/2] x86/process: Add proper bound checks in 64bit get_wchan()
Date: Wed, 30 Sep 2015 08:38:22 -0000 [thread overview]
Message-ID: <20150930083302.694788319@linutronix.de> (raw)
In-Reply-To: 20150930082754.401022511@linutronix.de
[-- Attachment #1: x86-process--Fix-bound-checks-in-64bit-get_wchan --]
[-- Type: text/plain, Size: 4438 bytes --]
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] #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 <dvyukov@google.com>
Reported-by: Sasha Levin <sasha.levin@oracle.com>
Based-on-patch-from: Wolfram Gloger <wmglo@dent.med.uni-muenchen.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
arch/x86/kernel/process_64.c | 52 ++++++++++++++++++++++++++++++++++---------
1 file changed, 42 insertions(+), 10 deletions(-)
Index: tip/arch/x86/kernel/process_64.c
===================================================================
--- tip.orig/arch/x86/kernel/process_64.c
+++ tip/arch/x86/kernel/process_64.c
@@ -499,27 +499,59 @@ void set_personality_ia32(bool x32)
}
EXPORT_SYMBOL_GPL(set_personality_ia32);
+/*
+ * Called from fs/proc with a reference on @p to find the function
+ * which called into schedule(). This needs to be done carefully
+ * because the task might wake up and we might look at a stack
+ * changing under us.
+ */
unsigned long get_wchan(struct task_struct *p)
{
- unsigned long stack;
- u64 fp, ip;
+ unsigned long start, bottom, top, sp, fp, ip;
int count = 0;
if (!p || p == current || p->state == TASK_RUNNING)
return 0;
- stack = (unsigned long)task_stack_page(p);
- if (p->thread.sp < stack || p->thread.sp >= stack+THREAD_SIZE)
+
+ start = (unsigned long)task_stack_page(p);
+ if (!start)
return 0;
- fp = *(u64 *)(p->thread.sp);
+
+ /*
+ * Layout of the stack page:
+ *
+ * ----------- topmax = start + THREAD_SIZE - sizeof(unsigned long)
+ * PADDING
+ * ----------- top = topmax - TOP_OF_KERNEL_STACK_PADDING
+ * stack
+ * ----------- bottom = start + sizeof(thread_info)
+ * thread_info
+ * ----------- start
+ *
+ * The tasks stack pointer points at the location where the
+ * framepointer is stored. The data on the stack is:
+ * ... IP FP ... IP FP
+ *
+ * We need to read FP and IP, so we need to adjust the upper
+ * bound by another unsigned long.
+ */
+ top = start + THREAD_SIZE - TOP_OF_KERNEL_STACK_PADDING;
+ top -= 2 * sizeof(unsigned long);
+ bottom = start + sizeof(struct thread_info);
+
+ sp = READ_ONCE(p->thread.sp);
+ if (sp < bottom || sp > top)
+ return 0;
+
+ fp = READ_ONCE(*(unsigned long *)sp);
do {
- if (fp < (unsigned long)stack ||
- fp >= (unsigned long)stack+THREAD_SIZE)
+ if (fp < bottom || fp > top)
return 0;
- ip = *(u64 *)(fp+8);
+ ip = READ_ONCE(*(unsigned long *)(fp + sizeof(unsigned long)));
if (!in_sched_functions(ip))
return ip;
- fp = *(u64 *)fp;
- } while (count++ < 16);
+ fp = READ_ONCE(*(unsigned long *)fp);
+ } while (count++ < 16 && p->state != TASK_RUNNING);
return 0;
}
next prev parent reply other threads:[~2015-09-30 8:39 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-09-30 8:38 [patch 0/2] x86/process: Sanitize bound checks in get_wchan() and unify 32/64 bit Thomas Gleixner
2015-09-30 8:38 ` Thomas Gleixner [this message]
2015-09-30 19:54 ` [tip:x86/urgent] x86/process: Add proper bound checks in 64bit get_wchan() tip-bot for Thomas Gleixner
2015-10-03 1:15 ` [patch 1/2] " Sasha Levin
2015-10-03 1:31 ` Andy Lutomirski
2015-10-03 10:54 ` Thomas Gleixner
2015-10-03 11:31 ` Andrey Ryabinin
2015-10-04 12:14 ` Dmitry Vyukov
[not found] ` <CAN=P9ph=u4YqxtK7iA3R12E86DVYBdZos+Yv0n6cw7E-ZU8x_g@mail.gmail.com>
2015-10-04 18:04 ` Dmitry Vyukov
2015-09-30 8:38 ` [patch 2/2] x86/process: Unify 32bit and 64bit implementations of get_wchan() Thomas Gleixner
2015-09-30 19:54 ` [tip:x86/urgent] " tip-bot for Thomas Gleixner
2015-09-30 9:06 ` [patch 0/2] x86/process: Sanitize bound checks in get_wchan() and unify 32/64 bit Borislav Petkov
2015-09-30 9:13 ` Dmitry Vyukov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20150930083302.694788319@linutronix.de \
--to=tglx@linutronix.de \
--cc=ak@linux.intel.com \
--cc=andreyknvl@google.com \
--cc=bp@alien8.de \
--cc=dvlasenk@redhat.com \
--cc=dvyukov@google.com \
--cc=glider@google.com \
--cc=kasan-dev@googlegroups.com \
--cc=kcc@google.com \
--cc=linux-kernel@vger.kernel.org \
--cc=luto@amacapital.net \
--cc=ryabinin.a.a@gmail.com \
--cc=sasha.levin@oracle.com \
--cc=wmglo@dent.med.uni-muenchen.de \
--cc=x86@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).