Re: [PATCH] x86, kasan: add KASAN checks to atomic operations

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Mar 6, 2017 at 1:42 PM, Dmitry Vyukov <dvyukov@xxxxxxxxxx> wrote:
> KASAN uses compiler instrumentation to intercept all memory accesses.
> But it does not see memory accesses done in assembly code.
> One notable user of assembly code is atomic operations. Frequently,
> for example, an atomic reference decrement is the last access to an
> object and a good candidate for a racy use-after-free.
>
> Add manual KASAN checks to atomic operations.
> Note: we need checks only before asm blocks and don't need them
> in atomic functions composed of other atomic functions
> (e.g. load-cmpxchg loops).

Peter, also pointed me at arch/x86/include/asm/bitops.h. Will add them in v2.


> Signed-off-by: Dmitry Vyukov <dvyukov@xxxxxxxxxx>
> Cc: Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx>
> Cc: Andrey Ryabinin <aryabinin@xxxxxxxxxxxxx>
> Cc: kasan-dev@xxxxxxxxxxxxxxxx
> Cc: linux-mm@xxxxxxxxx
> Cc: linux-kernel@xxxxxxxxxxxxxxx
>
> ---
> Within a day it has found its first bug:
>
> ==================================================================
> BUG: KASAN: use-after-free in atomic_dec_and_test
> arch/x86/include/asm/atomic.h:123 [inline] at addr ffff880079c30158
> BUG: KASAN: use-after-free in put_task_struct
> include/linux/sched/task.h:93 [inline] at addr ffff880079c30158
> BUG: KASAN: use-after-free in put_ctx+0xcf/0x110
> kernel/events/core.c:1131 at addr ffff880079c30158
> Write of size 4 by task syz-executor6/25698
> CPU: 2 PID: 25698 Comm: syz-executor6 Not tainted 4.10.0+ #302
> Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
> Call Trace:
>  __dump_stack lib/dump_stack.c:16 [inline]
>  dump_stack+0x2fb/0x3fd lib/dump_stack.c:52
>  kasan_object_err+0x1c/0x90 mm/kasan/report.c:166
>  print_address_description mm/kasan/report.c:208 [inline]
>  kasan_report_error mm/kasan/report.c:292 [inline]
>  kasan_report.part.2+0x1b0/0x460 mm/kasan/report.c:314
>  kasan_report+0x21/0x30 mm/kasan/report.c:301
>  check_memory_region_inline mm/kasan/kasan.c:326 [inline]
>  check_memory_region+0x139/0x190 mm/kasan/kasan.c:333
>  kasan_check_write+0x14/0x20 mm/kasan/kasan.c:344
>  atomic_dec_and_test arch/x86/include/asm/atomic.h:123 [inline]
>  put_task_struct include/linux/sched/task.h:93 [inline]
>  put_ctx+0xcf/0x110 kernel/events/core.c:1131
>  perf_event_release_kernel+0x3ad/0xc90 kernel/events/core.c:4322
>  perf_release+0x37/0x50 kernel/events/core.c:4338
>  __fput+0x332/0x800 fs/file_table.c:209
>  ____fput+0x15/0x20 fs/file_table.c:245
>  task_work_run+0x197/0x260 kernel/task_work.c:116
>  exit_task_work include/linux/task_work.h:21 [inline]
>  do_exit+0xb38/0x29c0 kernel/exit.c:880
>  do_group_exit+0x149/0x420 kernel/exit.c:984
>  get_signal+0x7e0/0x1820 kernel/signal.c:2318
>  do_signal+0xd2/0x2190 arch/x86/kernel/signal.c:808
>  exit_to_usermode_loop+0x200/0x2a0 arch/x86/entry/common.c:157
>  syscall_return_slowpath arch/x86/entry/common.c:191 [inline]
>  do_syscall_64+0x6fc/0x930 arch/x86/entry/common.c:286
>  entry_SYSCALL64_slow_path+0x25/0x25
> RIP: 0033:0x4458d9
> RSP: 002b:00007f3f07187cf8 EFLAGS: 00000246 ORIG_RAX: 00000000000000ca
> RAX: fffffffffffffe00 RBX: 00000000007080c8 RCX: 00000000004458d9
> RDX: 0000000000000000 RSI: 0000000000000000 RDI: 00000000007080c8
> RBP: 00000000007080a8 R08: 0000000000000000 R09: 0000000000000000
> R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
> R13: 0000000000000000 R14: 00007f3f071889c0 R15: 00007f3f07188700
> Object at ffff880079c30140, in cache task_struct size: 5376
> Allocated:
> PID = 25681
>  save_stack_trace+0x16/0x20 arch/x86/kernel/stacktrace.c:59
>  save_stack+0x43/0xd0 mm/kasan/kasan.c:513
>  set_track mm/kasan/kasan.c:525 [inline]
>  kasan_kmalloc+0xaa/0xd0 mm/kasan/kasan.c:616
>  kasan_slab_alloc+0x12/0x20 mm/kasan/kasan.c:555
>  kmem_cache_alloc_node+0x122/0x6f0 mm/slab.c:3662
>  alloc_task_struct_node kernel/fork.c:153 [inline]
>  dup_task_struct kernel/fork.c:495 [inline]
>  copy_process.part.38+0x19c8/0x4aa0 kernel/fork.c:1560
>  copy_process kernel/fork.c:1531 [inline]
>  _do_fork+0x200/0x1010 kernel/fork.c:1994
>  SYSC_clone kernel/fork.c:2104 [inline]
>  SyS_clone+0x37/0x50 kernel/fork.c:2098
>  do_syscall_64+0x2e8/0x930 arch/x86/entry/common.c:281
>  return_from_SYSCALL_64+0x0/0x7a
> Freed:
> PID = 25681
>  save_stack_trace+0x16/0x20 arch/x86/kernel/stacktrace.c:59
>  save_stack+0x43/0xd0 mm/kasan/kasan.c:513
>  set_track mm/kasan/kasan.c:525 [inline]
>  kasan_slab_free+0x6f/0xb0 mm/kasan/kasan.c:589
>  __cache_free mm/slab.c:3514 [inline]
>  kmem_cache_free+0x71/0x240 mm/slab.c:3774
>  free_task_struct kernel/fork.c:158 [inline]
>  free_task+0x151/0x1d0 kernel/fork.c:370
>  copy_process.part.38+0x18e5/0x4aa0 kernel/fork.c:1931
>  copy_process kernel/fork.c:1531 [inline]
>  _do_fork+0x200/0x1010 kernel/fork.c:1994
>  SYSC_clone kernel/fork.c:2104 [inline]
>  SyS_clone+0x37/0x50 kernel/fork.c:2098
>  do_syscall_64+0x2e8/0x930 arch/x86/entry/common.c:281
>  return_from_SYSCALL_64+0x0/0x7a
> ---
>  arch/x86/include/asm/atomic.h      | 11 +++++++++++
>  arch/x86/include/asm/atomic64_64.h | 10 ++++++++++
>  arch/x86/include/asm/cmpxchg.h     |  4 ++++
>  3 files changed, 25 insertions(+)
>
> diff --git a/arch/x86/include/asm/atomic.h b/arch/x86/include/asm/atomic.h
> index 14635c5ea025..64f0a7fb9b2f 100644
> --- a/arch/x86/include/asm/atomic.h
> +++ b/arch/x86/include/asm/atomic.h
> @@ -2,6 +2,7 @@
>  #define _ASM_X86_ATOMIC_H
>
>  #include <linux/compiler.h>
> +#include <linux/kasan-checks.h>
>  #include <linux/types.h>
>  #include <asm/alternative.h>
>  #include <asm/cmpxchg.h>
> @@ -47,6 +48,7 @@ static __always_inline void atomic_set(atomic_t *v, int i)
>   */
>  static __always_inline void atomic_add(int i, atomic_t *v)
>  {
> +       kasan_check_write(v, sizeof(*v));
>         asm volatile(LOCK_PREFIX "addl %1,%0"
>                      : "+m" (v->counter)
>                      : "ir" (i));
> @@ -61,6 +63,7 @@ static __always_inline void atomic_add(int i, atomic_t *v)
>   */
>  static __always_inline void atomic_sub(int i, atomic_t *v)
>  {
> +       kasan_check_write(v, sizeof(*v));
>         asm volatile(LOCK_PREFIX "subl %1,%0"
>                      : "+m" (v->counter)
>                      : "ir" (i));
> @@ -77,6 +80,7 @@ static __always_inline void atomic_sub(int i, atomic_t *v)
>   */
>  static __always_inline bool atomic_sub_and_test(int i, atomic_t *v)
>  {
> +       kasan_check_write(v, sizeof(*v));
>         GEN_BINARY_RMWcc(LOCK_PREFIX "subl", v->counter, "er", i, "%0", e);
>  }
>
> @@ -88,6 +92,7 @@ static __always_inline bool atomic_sub_and_test(int i, atomic_t *v)
>   */
>  static __always_inline void atomic_inc(atomic_t *v)
>  {
> +       kasan_check_write(v, sizeof(*v));
>         asm volatile(LOCK_PREFIX "incl %0"
>                      : "+m" (v->counter));
>  }
> @@ -100,6 +105,7 @@ static __always_inline void atomic_inc(atomic_t *v)
>   */
>  static __always_inline void atomic_dec(atomic_t *v)
>  {
> +       kasan_check_write(v, sizeof(*v));
>         asm volatile(LOCK_PREFIX "decl %0"
>                      : "+m" (v->counter));
>  }
> @@ -114,6 +120,7 @@ static __always_inline void atomic_dec(atomic_t *v)
>   */
>  static __always_inline bool atomic_dec_and_test(atomic_t *v)
>  {
> +       kasan_check_write(v, sizeof(*v));
>         GEN_UNARY_RMWcc(LOCK_PREFIX "decl", v->counter, "%0", e);
>  }
>
> @@ -127,6 +134,7 @@ static __always_inline bool atomic_dec_and_test(atomic_t *v)
>   */
>  static __always_inline bool atomic_inc_and_test(atomic_t *v)
>  {
> +       kasan_check_write(v, sizeof(*v));
>         GEN_UNARY_RMWcc(LOCK_PREFIX "incl", v->counter, "%0", e);
>  }
>
> @@ -141,6 +149,7 @@ static __always_inline bool atomic_inc_and_test(atomic_t *v)
>   */
>  static __always_inline bool atomic_add_negative(int i, atomic_t *v)
>  {
> +       kasan_check_write(v, sizeof(*v));
>         GEN_BINARY_RMWcc(LOCK_PREFIX "addl", v->counter, "er", i, "%0", s);
>  }
>
> @@ -194,6 +203,7 @@ static inline int atomic_xchg(atomic_t *v, int new)
>  #define ATOMIC_OP(op)                                                  \
>  static inline void atomic_##op(int i, atomic_t *v)                     \
>  {                                                                      \
> +       kasan_check_write(v, sizeof(*v));                               \
>         asm volatile(LOCK_PREFIX #op"l %1,%0"                           \
>                         : "+m" (v->counter)                             \
>                         : "ir" (i)                                      \
> @@ -258,6 +268,7 @@ static __always_inline int __atomic_add_unless(atomic_t *v, int a, int u)
>   */
>  static __always_inline short int atomic_inc_short(short int *v)
>  {
> +       kasan_check_write(v, sizeof(*v));
>         asm(LOCK_PREFIX "addw $1, %0" : "+m" (*v));
>         return *v;
>  }
> diff --git a/arch/x86/include/asm/atomic64_64.h b/arch/x86/include/asm/atomic64_64.h
> index 89ed2f6ae2f7..13fe8ff5a126 100644
> --- a/arch/x86/include/asm/atomic64_64.h
> +++ b/arch/x86/include/asm/atomic64_64.h
> @@ -2,6 +2,7 @@
>  #define _ASM_X86_ATOMIC64_64_H
>
>  #include <linux/types.h>
> +#include <linux/kasan-checks.h>
>  #include <asm/alternative.h>
>  #include <asm/cmpxchg.h>
>
> @@ -42,6 +43,7 @@ static inline void atomic64_set(atomic64_t *v, long i)
>   */
>  static __always_inline void atomic64_add(long i, atomic64_t *v)
>  {
> +       kasan_check_write(v, sizeof(*v));
>         asm volatile(LOCK_PREFIX "addq %1,%0"
>                      : "=m" (v->counter)
>                      : "er" (i), "m" (v->counter));
> @@ -56,6 +58,7 @@ static __always_inline void atomic64_add(long i, atomic64_t *v)
>   */
>  static inline void atomic64_sub(long i, atomic64_t *v)
>  {
> +       kasan_check_write(v, sizeof(*v));
>         asm volatile(LOCK_PREFIX "subq %1,%0"
>                      : "=m" (v->counter)
>                      : "er" (i), "m" (v->counter));
> @@ -72,6 +75,7 @@ static inline void atomic64_sub(long i, atomic64_t *v)
>   */
>  static inline bool atomic64_sub_and_test(long i, atomic64_t *v)
>  {
> +       kasan_check_write(v, sizeof(*v));
>         GEN_BINARY_RMWcc(LOCK_PREFIX "subq", v->counter, "er", i, "%0", e);
>  }
>
> @@ -83,6 +87,7 @@ static inline bool atomic64_sub_and_test(long i, atomic64_t *v)
>   */
>  static __always_inline void atomic64_inc(atomic64_t *v)
>  {
> +       kasan_check_write(v, sizeof(*v));
>         asm volatile(LOCK_PREFIX "incq %0"
>                      : "=m" (v->counter)
>                      : "m" (v->counter));
> @@ -96,6 +101,7 @@ static __always_inline void atomic64_inc(atomic64_t *v)
>   */
>  static __always_inline void atomic64_dec(atomic64_t *v)
>  {
> +       kasan_check_write(v, sizeof(*v));
>         asm volatile(LOCK_PREFIX "decq %0"
>                      : "=m" (v->counter)
>                      : "m" (v->counter));
> @@ -111,6 +117,7 @@ static __always_inline void atomic64_dec(atomic64_t *v)
>   */
>  static inline bool atomic64_dec_and_test(atomic64_t *v)
>  {
> +       kasan_check_write(v, sizeof(*v));
>         GEN_UNARY_RMWcc(LOCK_PREFIX "decq", v->counter, "%0", e);
>  }
>
> @@ -124,6 +131,7 @@ static inline bool atomic64_dec_and_test(atomic64_t *v)
>   */
>  static inline bool atomic64_inc_and_test(atomic64_t *v)
>  {
> +       kasan_check_write(v, sizeof(*v));
>         GEN_UNARY_RMWcc(LOCK_PREFIX "incq", v->counter, "%0", e);
>  }
>
> @@ -138,6 +146,7 @@ static inline bool atomic64_inc_and_test(atomic64_t *v)
>   */
>  static inline bool atomic64_add_negative(long i, atomic64_t *v)
>  {
> +       kasan_check_write(v, sizeof(*v));
>         GEN_BINARY_RMWcc(LOCK_PREFIX "addq", v->counter, "er", i, "%0", s);
>  }
>
> @@ -233,6 +242,7 @@ static inline long atomic64_dec_if_positive(atomic64_t *v)
>  #define ATOMIC64_OP(op)                                                        \
>  static inline void atomic64_##op(long i, atomic64_t *v)                        \
>  {                                                                      \
> +       kasan_check_write(v, sizeof(*v));                               \
>         asm volatile(LOCK_PREFIX #op"q %1,%0"                           \
>                         : "+m" (v->counter)                             \
>                         : "er" (i)                                      \
> diff --git a/arch/x86/include/asm/cmpxchg.h b/arch/x86/include/asm/cmpxchg.h
> index 97848cdfcb1a..a10e7fb09210 100644
> --- a/arch/x86/include/asm/cmpxchg.h
> +++ b/arch/x86/include/asm/cmpxchg.h
> @@ -2,6 +2,7 @@
>  #define ASM_X86_CMPXCHG_H
>
>  #include <linux/compiler.h>
> +#include <linux/kasan-checks.h>
>  #include <asm/cpufeatures.h>
>  #include <asm/alternative.h> /* Provides LOCK_PREFIX */
>
> @@ -41,6 +42,7 @@ extern void __add_wrong_size(void)
>  #define __xchg_op(ptr, arg, op, lock)                                  \
>         ({                                                              \
>                 __typeof__ (*(ptr)) __ret = (arg);                      \
> +               kasan_check_write((void *)(ptr), sizeof(*(ptr)));       \
>                 switch (sizeof(*(ptr))) {                               \
>                 case __X86_CASE_B:                                      \
>                         asm volatile (lock #op "b %b0, %1\n"            \
> @@ -86,6 +88,7 @@ extern void __add_wrong_size(void)
>         __typeof__(*(ptr)) __ret;                                       \
>         __typeof__(*(ptr)) __old = (old);                               \
>         __typeof__(*(ptr)) __new = (new);                               \
> +       kasan_check_write((void *)(ptr), sizeof(*(ptr)));               \
>         switch (size) {                                                 \
>         case __X86_CASE_B:                                              \
>         {                                                               \
> @@ -171,6 +174,7 @@ extern void __add_wrong_size(void)
>         BUILD_BUG_ON(sizeof(*(p2)) != sizeof(long));                    \
>         VM_BUG_ON((unsigned long)(p1) % (2 * sizeof(long)));            \
>         VM_BUG_ON((unsigned long)((p1) + 1) != (unsigned long)(p2));    \
> +       kasan_check_write((void *)(p1), 2 * sizeof(*(p1)));             \
>         asm volatile(pfx "cmpxchg%c4b %2; sete %0"                      \
>                      : "=a" (__ret), "+d" (__old2),                     \
>                        "+m" (*(p1)), "+m" (*(p2))                       \
> --
> 2.12.0.rc1.440.g5b76565f74-goog
>

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@xxxxxxxxx.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@xxxxxxxxx";> email@xxxxxxxxx </a>



[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [eCos]     [Asterisk Internet PBX]     [Linux API]
  Powered by Linux