| From 7c4788950ba5922fde976d80b72baf46f14dee8d Mon Sep 17 00:00:00 2001 |
| From: Peter Zijlstra <peterz@infradead.org> |
| Date: Tue, 22 Nov 2016 10:57:15 +0100 |
| Subject: x86/uaccess, sched/preempt: Verify access_ok() context |
| |
| From: Peter Zijlstra <peterz@infradead.org> |
| |
| commit 7c4788950ba5922fde976d80b72baf46f14dee8d upstream. |
| |
| I recently encountered wreckage because access_ok() was used where it |
| should not be, add an explicit WARN when access_ok() is used wrongly. |
| |
| Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> |
| Cc: Andy Lutomirski <luto@amacapital.net> |
| Cc: H. Peter Anvin <hpa@zytor.com> |
| Cc: Linus Torvalds <torvalds@linux-foundation.org> |
| Cc: Peter Zijlstra <peterz@infradead.org> |
| Cc: Thomas Gleixner <tglx@linutronix.de> |
| Cc: linux-kernel@vger.kernel.org |
| Signed-off-by: Ingo Molnar <mingo@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| arch/x86/include/asm/uaccess.h | 13 +++++++++++-- |
| include/linux/preempt.h | 21 +++++++++++++-------- |
| 2 files changed, 24 insertions(+), 10 deletions(-) |
| |
| --- a/arch/x86/include/asm/uaccess.h |
| +++ b/arch/x86/include/asm/uaccess.h |
| @@ -68,6 +68,12 @@ static inline bool __chk_range_not_ok(un |
| __chk_range_not_ok((unsigned long __force)(addr), size, limit); \ |
| }) |
| |
| +#ifdef CONFIG_DEBUG_ATOMIC_SLEEP |
| +# define WARN_ON_IN_IRQ() WARN_ON_ONCE(!in_task()) |
| +#else |
| +# define WARN_ON_IN_IRQ() |
| +#endif |
| + |
| /** |
| * access_ok: - Checks if a user space pointer is valid |
| * @type: Type of access: %VERIFY_READ or %VERIFY_WRITE. Note that |
| @@ -88,8 +94,11 @@ static inline bool __chk_range_not_ok(un |
| * checks that the pointer is in the user space range - after calling |
| * this function, memory access functions may still return -EFAULT. |
| */ |
| -#define access_ok(type, addr, size) \ |
| - likely(!__range_not_ok(addr, size, user_addr_max())) |
| +#define access_ok(type, addr, size) \ |
| +({ \ |
| + WARN_ON_IN_IRQ(); \ |
| + likely(!__range_not_ok(addr, size, user_addr_max())); \ |
| +}) |
| |
| /* |
| * These are the main single-value transfer routines. They automatically |
| --- a/include/linux/preempt.h |
| +++ b/include/linux/preempt.h |
| @@ -65,19 +65,24 @@ |
| |
| /* |
| * Are we doing bottom half or hardware interrupt processing? |
| - * Are we in a softirq context? Interrupt context? |
| - * in_softirq - Are we currently processing softirq or have bh disabled? |
| - * in_serving_softirq - Are we currently processing softirq? |
| + * |
| + * in_irq() - We're in (hard) IRQ context |
| + * in_softirq() - We have BH disabled, or are processing softirqs |
| + * in_interrupt() - We're in NMI,IRQ,SoftIRQ context or have BH disabled |
| + * in_serving_softirq() - We're in softirq context |
| + * in_nmi() - We're in NMI context |
| + * in_task() - We're in task context |
| + * |
| + * Note: due to the BH disabled confusion: in_softirq(),in_interrupt() really |
| + * should not be used in new code. |
| */ |
| #define in_irq() (hardirq_count()) |
| #define in_softirq() (softirq_count()) |
| #define in_interrupt() (irq_count()) |
| #define in_serving_softirq() (softirq_count() & SOFTIRQ_OFFSET) |
| - |
| -/* |
| - * Are we in NMI context? |
| - */ |
| -#define in_nmi() (preempt_count() & NMI_MASK) |
| +#define in_nmi() (preempt_count() & NMI_MASK) |
| +#define in_task() (!(preempt_count() & \ |
| + (NMI_MASK | HARDIRQ_MASK | SOFTIRQ_OFFSET))) |
| |
| /* |
| * The preempt_count offset after preempt_disable(); |