当前位置:   article > 正文

linux kernel 内存踩踏之KASAN(一)

linux kernel 内存踩踏之KASAN(一)

一、背景

linux 内核出现内存类问题时,我们常用的调试工具就是kasan,kasan有三种模式:

1. Generic KASAN (这个就是我们最常用的,1 debug byte indicate 8 bytes use state, 对标用户层 asan)

2. Software Tag-Based KASAN (这个可以对标用户层 hwasan,仅64位生效)

3. Hardware Tag-Based KASAN (大名鼎鼎的MTE, 也是arm64 且硬件平台需要额外支持)

后面将对比三种不同的kasan 使用,实现原理以及使用案例

二、KASAN使能相关配置(Generic版本)

kasan相关config是否打开

  1. /dev # zcat /proc/config.gz | grep -i kasan
  2. CONFIG_KASAN_SHADOW_OFFSET=0xdfff800000000000 //这个offset怎来来的什么含义?下一节描述
  3. CONFIG_DRIVER_KASAN_TEST=m
  4. CONFIG_HAVE_ARCH_KASAN=y
  5. CONFIG_HAVE_ARCH_KASAN_SW_TAGS=y
  6. CONFIG_HAVE_ARCH_KASAN_HW_TAGS=y
  7. CONFIG_HAVE_ARCH_KASAN_VMALLOC=y
  8. CONFIG_CC_HAS_KASAN_GENERIC=y
  9. CONFIG_CC_HAS_KASAN_SW_TAGS=y
  10. CONFIG_KASAN=y
  11. CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX=y
  12. CONFIG_KASAN_GENERIC=y //标准版本kasan
  13. # CONFIG_KASAN_SW_TAGS is not set
  14. # CONFIG_KASAN_HW_TAGS is not set
  15. CONFIG_KASAN_OUTLINE=y
  16. # CONFIG_KASAN_INLINE is not set
  17. CONFIG_KASAN_STACK=y //stack kasan检测,如局部变量,局部数组等操作引起的内存踩踏
  18. CONFIG_KASAN_VMALLOC=y //vmalloc kasan检测,使用vmalloc申请内存的内存踩踏

这里使用的是普通版本 Generic KASAN

三、kasan基本原理

shadow byte 的值:

1~7 表示8byte可使用的情况

0表示8byte均可使用

其他值表示无法使用,常见的shadow byte值可以看mm/kasan/kasan.h定义:

  1. #ifdef CONFIG_KASAN_GENERIC
  2. #define KASAN_PAGE_FREE 0xFF /* freed page */
  3. #define KASAN_PAGE_REDZONE 0xFE /* redzone for kmalloc_large allocation */
  4. #define KASAN_SLAB_REDZONE 0xFC /* redzone for slab object */
  5. #define KASAN_SLAB_FREE 0xFB /* freed slab object */
  6. #define KASAN_VMALLOC_INVALID 0xF8 /* inaccessible space in vmap area */
  7. #define KASAN_SLAB_FREETRACK 0xFA /* freed slab object with free track */
  8. #define KASAN_GLOBAL_REDZONE 0xF9 /* redzone for global variable */
  9. /* Stack redzone shadow values. Compiler ABI, do not change. */
  10. #define KASAN_STACK_LEFT 0xF1
  11. #define KASAN_STACK_MID 0xF2
  12. #define KASAN_STACK_RIGHT 0xF3
  13. #define KASAN_STACK_PARTIAL 0xF4
  14. /* alloca redzone shadow values. */
  15. #define KASAN_ALLOCA_LEFT 0xCA
  16. #define KASAN_ALLOCA_RIGHT 0xCB

下图是arm64 48位 pagesize 4K的内存映射图,正好32TB映射整个内核空间。

前面一节遗留一个问题,CONFIG_KASAN_SHADOW_OFFSET=0xdfff800000000000

这个shadowoffset 是用来看什么的?这个实际是根据我们内核虚拟地址位数,kasan模式计算而来的

  1. config KASAN_SHADOW_OFFSET
  2. hex
  3. depends on KASAN_GENERIC || KASAN_SW_TAGS
  4. default 0xdfff800000000000 if (ARM64_VA_BITS_48 || ARM64_VA_BITS_52) && !KASAN_SW_TAGS
  5. default 0xdfffc00000000000 if ARM64_VA_BITS_47 && !KASAN_SW_TAGS
  6. default 0xdffffe0000000000 if ARM64_VA_BITS_42 && !KASAN_SW_TAGS
  7. default 0xdfffffc000000000 if ARM64_VA_BITS_39 && !KASAN_SW_TAGS
  8. default 0xdffffff800000000 if ARM64_VA_BITS_36 && !KASAN_SW_TAGS
  9. default 0xefff800000000000 if (ARM64_VA_BITS_48 || ARM64_VA_BITS_52) && KASAN_SW_TAGS
  10. default 0xefffc00000000000 if ARM64_VA_BITS_47 && KASAN_SW_TAGS
  11. default 0xeffffe0000000000 if ARM64_VA_BITS_42 && KASAN_SW_TAGS
  12. default 0xefffffc000000000 if ARM64_VA_BITS_39 && KASAN_SW_TAGS
  13. default 0xeffffff800000000 if ARM64_VA_BITS_36 && KASAN_SW_TAGS
  14. default 0xfffffffffffffff

计算方法是:

CONFIG_KASAN_SHADOW_OFFSET= KASAN_SHADOW_START - KERNEL_ADDR_START >>3

= 0xffff600000000000 - ( 0xffff000000000000 >> 3) = 0xdfff800000000000

有了这个kasan_shadow_offset, 后面我们需要获取一个内核地址对应的shadow 位置,只需要通过公式:

kernel_addr >> 3 + CONFIG_KASAN_SHADOW_OFFSET = kernel_addr对应的shadow_addr

四、利用 test driver程序验证

下面是一个简易的测试用例,用来测试kmalloc、page、全局变量、stack变量和vmalloc的内存踩踏

  1. #include <linux/module.h>
  2. #include <linux/kernel.h>
  3. #include <linux/init.h>
  4. #include <linux/miscdevice.h>
  5. #include <linux/workqueue.h>
  6. #include <linux/jiffies.h>
  7. #include <asm/page.h>
  8. #include <linux/vmalloc.h>
  9. #include "../../../mm/kasan/kasan.h"
  10. int global_kasan_value[2] = {996, 007};
  11. struct kasan_test_type {
  12. int type;
  13. };
  14. static struct kasan_test_type *gptr = NULL;
  15. enum kasan_test_case{
  16. slab_out_of_bounds = 0,
  17. page_out_of_bounds = 1,
  18. global_out_of_bounds = 2,
  19. stack_out_of_bounds = 3,
  20. use_after_free = 4,
  21. vmalloc_out_of_bounds = 5,
  22. alloca_out_of_bounds = 6,
  23. };
  24. static void kmalloc_oob_right(size_t size, int write_offset)
  25. {
  26. char *ptr;
  27. ptr = kmalloc(size, GFP_KERNEL);
  28. pr_info("%s %llx\n", __func__, (unsigned long long)ptr);
  29. ptr[size - 1 + write_offset] = 'y';
  30. kfree(ptr);
  31. }
  32. static void global_oob_left(void)
  33. {
  34. pr_info("global arr oob access %d\n", global_kasan_value[2]);
  35. }
  36. static void pagealloc_oob_right(size_t order)
  37. {
  38. char *ptr;
  39. struct page *pages;
  40. size_t size = (1UL << (PAGE_SHIFT + order));
  41. pages = alloc_pages(GFP_KERNEL, order);
  42. ptr = page_address(pages);
  43. pr_info("%s %llx\n", __func__, (unsigned long long)ptr);
  44. ptr[0] = ptr[size];
  45. free_pages((unsigned long)ptr, order);
  46. }
  47. static void pagealloc_uaf(size_t order)
  48. {
  49. char *ptr;
  50. struct page *pages;
  51. pages = alloc_pages(GFP_KERNEL, order);
  52. ptr = page_address(pages);
  53. pr_info("%s %llx", __func__, (unsigned long long)ptr);
  54. free_pages((unsigned long)ptr, order);
  55. pr_info("%s %d\n", __func__, ptr[0]);
  56. }
  57. static void vmalloc_oob(size_t size)
  58. {
  59. char *v_ptr;
  60. v_ptr = vmalloc(size);
  61. OPTIMIZER_HIDE_VAR(v_ptr);
  62. pr_info("%s %llx", __func__, (unsigned long long)v_ptr);
  63. /* Make sure in-bounds accesses are valid. */
  64. v_ptr[0] = 0;
  65. v_ptr[size - 1] = 0;
  66. /* trigger oob access */
  67. pr_info("%s %d\n", __func__, v_ptr[size]);
  68. vfree(v_ptr);
  69. }
  70. static void kasan_stack_oob(void)
  71. {
  72. char stack_array[10];
  73. /* See comment in kasan_global_oob_right. */
  74. char *volatile array = stack_array;
  75. char *p = &array[ARRAY_SIZE(stack_array) + 4];
  76. pr_info("%s %d\n", __func__, *p);
  77. }
  78. static void kasan_test_case(int type)
  79. {
  80. //使能kasan多次检测,默认只上报一次
  81. bool multishot = kasan_save_enable_multi_shot();
  82. switch(type) {
  83. case slab_out_of_bounds:
  84. kmalloc_oob_right(128, 2); //alloc 128 byte and overwrite 2 offset
  85. break;
  86. case page_out_of_bounds:
  87. pagealloc_oob_right(0);
  88. break;
  89. case global_out_of_bounds:
  90. global_oob_left();
  91. break;
  92. case stack_out_of_bounds:
  93. kasan_stack_oob();
  94. break;
  95. case use_after_free:
  96. pagealloc_uaf(0);
  97. break;
  98. case vmalloc_out_of_bounds:
  99. vmalloc_oob(2048);
  100. break;
  101. default :
  102. pr_info("undef error type %d\n", type);
  103. break;
  104. }
  105. kasan_restore_multi_shot(multishot);
  106. pr_info("%s type %d\n", __func__, type);
  107. }
  108. static ssize_t kasan_testcase_write(struct file *filp, const char __user *buf,
  109. size_t len, loff_t *off)
  110. {
  111. char *kbuf;
  112. int ntcase;
  113. kbuf = kmalloc(len + 1, GFP_KERNEL);
  114. if (copy_from_user(kbuf, buf, len) != 0) {
  115. pr_info("copy the buff failed \n");
  116. goto done;
  117. }
  118. ntcase = simple_strtoul(kbuf, NULL, 0);
  119. kasan_test_case(ntcase);
  120. done:
  121. return len;
  122. }
  123. static struct file_operations kasan_fops = {
  124. .owner = THIS_MODULE,
  125. .write = kasan_testcase_write,
  126. .llseek = noop_llseek,
  127. };
  128. static struct miscdevice kasan_misc = {
  129. .minor = MISC_DYNAMIC_MINOR,
  130. .name = "kasan_test",
  131. .fops = &kasan_fops,
  132. };
  133. static int __init kasan_start(void)
  134. {
  135. int ret;
  136. ret = misc_register(&kasan_misc);
  137. if (ret < 0) {
  138. printk(KERN_EMERG " kasan test register failed %d\n", ret);
  139. return ret;
  140. }
  141. gptr = kzalloc(sizeof(struct kasan_test_type), GFP_KERNEL);
  142. printk(KERN_INFO "kasan test register\n");
  143. return 0;
  144. }
  145. static void __exit kasan_end(void)
  146. {
  147. misc_deregister(&kasan_misc);
  148. }
  149. MODULE_LICENSE("GPL");
  150. MODULE_AUTHOR("geek");
  151. MODULE_DESCRIPTION("A simple kasan test driver!");
  152. MODULE_VERSION("0.1");
  153. module_init(kasan_start);
  154. module_exit(kasan_end);

五、内存踩踏testcase调试

触发kmalloc的out of bound访问

  1. /dev # echo 0 > /dev/kasan_test
  2. [ 4063.037612] kmalloc_oob_right ffff000006e57400
  3. [ 4063.065278] ==================================================================
  4. [ 4063.073081] BUG: KASAN: slab-out-of-bounds in kasan_testcase_write+0x170/0x4d8 [kasan_driver]
  5. [ 4063.075812] Write of size 1 at addr ffff000006e57481 by task sh/179
  6. [ 4063.076529]
  7. [ 4063.077151] CPU: 5 PID: 179 Comm: sh Tainted: G B N 6.6.1-g3cba94c761ec-dirty #15
  8. [ 4063.077902] Hardware name: linux,dummy-virt (DT)
  9. [ 4063.078538] Call trace:
  10. [ 4063.078926] dump_backtrace+0x90/0xe8
  11. [ 4063.079771] show_stack+0x18/0x24
  12. [ 4063.079971] dump_stack_lvl+0x48/0x60
  13. [ 4063.080175] print_report+0xf8/0x5d8
  14. [ 4063.080372] kasan_report+0xc4/0x108
  15. [ 4063.080566] __asan_store1+0x60/0x6c
  16. [ 4063.080783] kasan_testcase_write+0x170/0x4d8 [kasan_driver]
  17. [ 4063.081141] vfs_write+0x158/0x45c
  18. [ 4063.081492] ksys_write+0xd0/0x180
  19. [ 4063.081835] __arm64_sys_write+0x44/0x58
  20. [ 4063.082188] invoke_syscall+0x60/0x184
  21. [ 4063.082550] el0_svc_common.constprop.0+0x78/0x13c
  22. [ 4063.082955] do_el0_svc+0x30/0x40
  23. [ 4063.083179] el0_svc+0x38/0x70
  24. [ 4063.083351] el0t_64_sync_handler+0x120/0x12c
  25. [ 4063.083553] el0t_64_sync+0x190/0x194
  26. [ 4063.083853]
  27. [ 4063.083982] Allocated by task 179:
  28. [ 4063.084229] kasan_save_stack+0x3c/0x64
  29. [ 4063.084559] kasan_set_track+0x2c/0x40
  30. [ 4063.084757] kasan_save_alloc_info+0x24/0x34
  31. [ 4063.084975] __kasan_kmalloc+0xb8/0xbc
  32. [ 4063.085230] kmalloc_trace+0x48/0x5c
  33. [ 4063.085438] kasan_testcase_write+0x154/0x4d8 [kasan_driver]
  34. [ 4063.085758] vfs_write+0x158/0x45c
  35. [ 4063.085965] ksys_write+0xd0/0x180
  36. [ 4063.086155] __arm64_sys_write+0x44/0x58
  37. [ 4063.086355] invoke_syscall+0x60/0x184
  38. [ 4063.086556] el0_svc_common.constprop.0+0x78/0x13c
  39. [ 4063.086790] do_el0_svc+0x30/0x40
  40. [ 4063.086984] el0_svc+0x38/0x70
  41. [ 4063.087168] el0t_64_sync_handler+0x120/0x12c
  42. [ 4063.087385] el0t_64_sync+0x190/0x194
  43. [ 4063.087600]
  44. [ 4063.087749] The buggy address belongs to the object at ffff000006e57400
  45. [ 4063.087749] which belongs to the cache kmalloc-128 of size 128
  46. [ 4063.088269] The buggy address is located 1 bytes to the right of
  47. [ 4063.088269] allocated 128-byte region [ffff000006e57400, ffff000006e57480)
  48. [ 4063.088708]
  49. [ 4063.088928] The buggy address belongs to the physical page:
  50. [ 4063.089384] page:(____ptrval____) refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x46e56
  51. [ 4063.089990] head:(____ptrval____) order:1 entire_mapcount:0 nr_pages_mapped:0 pincount:0
  52. [ 4063.090330] flags: 0x3fffc0000000840(slab|head|node=0|zone=0|lastcpupid=0xffff)
  53. [ 4063.090960] page_type: 0xffffffff()
  54. [ 4063.091467] raw: 03fffc0000000840 ffff0000060028c0 dead000000000122 0000000000000000
  55. [ 4063.091776] raw: 0000000000000000 0000000080200020 00000001ffffffff 0000000000000000
  56. [ 4063.092095] page dumped because: kasan: bad access detected
  57. [ 4063.092322]
  58. [ 4063.092443] Memory state around the buggy address:
  59. [ 4063.092785] ffff000006e57380: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
  60. [ 4063.093109] ffff000006e57400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  61. [ 4063.093404] >ffff000006e57480: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
  62. [ 4063.093690] ^
  63. [ 4063.093906] ffff000006e57500: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
  64. [ 4063.094188] ffff000006e57580: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
  65. [ 4063.094470] ==================================================================
  66. [ 4063.096441] kasan_test_case type 0

对应代码

  1. static void kmalloc_oob_right(size_t size, int write_offset)
  2. {
  3. char *ptr;
  4. ptr = kmalloc(size, GFP_KERNEL);
  5. pr_info("%s %llx\n", __func__, (unsigned long long)ptr);
  6. ptr[size - 1 + write_offset] = 'y'; //触发越界访问
  7. ......
  8. 对应汇编代码
  9. 0xffff80007dbf0174 <+336>: bl 0xffff800080321be4 <kmalloc_trace> //1.这里完成分配内存的shadow标记
  10. 0xffff80007dbf0178 <+340>: mov x2, x0
  11. 0xffff80007dbf017c <+344>: add x1, x22, #0x80
  12. 0xffff80007dbf0180 <+348>: mov x24, x0
  13. 0xffff80007dbf0184 <+352>: add x0, x22, #0xc0
  14. 0xffff80007dbf0188 <+356>: bl 0xffff800080154834 <_printk>
  15. 0xffff80007dbf018c <+360>: add x0, x24, #0x81
  16. 0xffff80007dbf0190 <+364>: bl 0xffff8000803b70d8 <__asan_store1> //2.这里来检查内存访问是否合法
  17. 0xffff80007dbf0194 <+368>: mov w1, #0x79 // #121
  18. 0xffff80007dbf0198 <+372>: strb w1, [x24, #129]
  19. 0xffff80007dbf019c <+376>: mov x0, x24
  20. 0xffff80007dbf01a0 <+380>: bl 0xffff800080322a8c <kfree>

这里实际分成两步:

a、在kmalloc时设置shadow标记;

b、在访问时根据指针操作的范围给kasan检查传入指针和长度的检查,对比tag标记确认指针操作是否合法

5.1.kmalloc时设置tag 标记分析:

  1. __kmalloc
  2. -->kmalloc_slab
  3. -->__kasan_kmalloc
  4. -->kasan_poison_last_granule
  5. -->kasan_poison
  6. Dump of assembler code for function kasan_poison_last_granule:
  7. 0xffff8000803b89ec <+0>: ands x2, x1, #0x7
  8. 0xffff8000803b89f0 <+4>: b.eq 0xffff8000803b8a08 <kasan_poison_last_granule+28> // b.none
  9. 0xffff8000803b89f4 <+8>: add x0, x0, x1
  10. 0xffff8000803b89f8 <+12>: mov x1, #0x800000000000 // 熟悉的0xdfff800000000000
  11. 0xffff8000803b89fc <+16>: movk x1, #0xdfff, lsl #48
  12. 0xffff8000803b8a00 <+20>: lsr x0, x0, #3
  13. 0xffff8000803b8a04 <+24>: strb w2, [x0, x1]
  14. 0xffff8000803b8a08 <+28>: ret

上面的代码完成ptr>>3 然后根据 size 长度,填充tag到shadow的地址:ptr>>3 + kasan_shadow_offset(0xdfff800000000000)

比如上面的kmalloc 128字节,指针值是0xffff000006e57400,我们查看它的shadow标记值,正好shadow值对应16个0(16*8 可用byte) :

0xffff000006e57400对应shadow值

5.2.内存访问时kasan是如何捕获异常

  1. 对应汇编代码
  2. 0xffff80007dbf0174 <+336>: bl 0xffff800080321be4 <kmalloc_trace> //1.这里完成分配内存的shadow标记
  3. 0xffff80007dbf0178 <+340>: mov x2, x0
  4. 0xffff80007dbf017c <+344>: add x1, x22, #0x80
  5. 0xffff80007dbf0180 <+348>: mov x24, x0
  6. 0xffff80007dbf0184 <+352>: add x0, x22, #0xc0
  7. 0xffff80007dbf0188 <+356>: bl 0xffff800080154834 <_printk>
  8. 0xffff80007dbf018c <+360>: add x0, x24, #0x81 //注意这里传入的0x81,表示指针访问长度
  9. 0xffff80007dbf0190 <+364>: bl 0xffff8000803b70d8 <__asan_store1> //2.这里来检查内存访问是否合法
  10. 0xffff80007dbf0194 <+368>: mov w1, #0x79 // #121
  11. 0xffff80007dbf0198 <+372>: strb w1, [x24, #129]
  12. 0xffff80007dbf019c <+376>: mov x0, x24
  13. 0xffff80007dbf01a0 <+380>: bl 0xffff800080322a8c <kfree>
  14. __asan_store1实现:
  15. Dump of assembler code for function __asan_store1:
  16. 0xffff8000803b70d8 <+0>: paciasp
  17. 0xffff8000803b70dc <+4>: stp x29, x30, [sp, #-16]!
  18. 0xffff8000803b70e0 <+8>: xpaclri
  19. 0xffff8000803b70e4 <+12>: mov x29, sp
  20. 0xffff8000803b70e8 <+16>: cmn x0, #0x1
  21. 0xffff8000803b70ec <+20>: b.cs 0xffff8000803b7128 <__asan_store1+80> // b.hs, b.nlast
  22. 0xffff8000803b70f0 <+24>: mov x2, #0xfffeffffffffffff // #-281474976710657
  23. 0xffff8000803b70f4 <+28>: cmp x0, x2
  24. 0xffff8000803b70f8 <+32>: b.ls 0xffff8000803b7128 <__asan_store1+80> // b.plast
  25. 0xffff8000803b70fc <+36>: lsr x3, x0, #3 // 1.x0指针右移3位后存放在x3
  26. 0xffff8000803b7100 <+40>: mov x2, #0x800000000000 // 2.X2 存储kasan_offset 0xdfff800000000000
  27. 0xffff8000803b7104 <+44>: movk x2, #0xdfff, lsl #48
  28. 0xffff8000803b7108 <+48>: ldrsb w2, [x3, x2] // 3.读取x3+x2地址的值,即tag值
  29. 0xffff8000803b710c <+52>: cbnz w2, 0xffff8000803b711c <__asan_store1+68>
  30. 0xffff8000803b7110 <+56>: ldp x29, x30, [sp], #16
  31. 0xffff8000803b7114 <+60>: autiasp
  32. 0xffff8000803b7118 <+64>: ret
  33. 0xffff8000803b711c <+68>: and w1, w0, #0x7 //4.取待访问指针访问长度的一字节访问长度
  34. 0xffff8000803b7120 <+72>: cmp w2, w1 //5.和shadow值做比较
  35. 0xffff8000803b7124 <+76>: b.gt 0xffff8000803b7110 <__asan_store1+56>
  36. 0xffff8000803b7128 <+80>: mov x3, x30
  37. 0xffff8000803b712c <+84>: mov w2, #0x1 // #1
  38. 0xffff8000803b7130 <+88>: mov x1, #0x1 // #1
  39. 0xffff8000803b7134 <+92>: bl 0xffff8000803b67a0 <kasan_report> //6、shadow允许访问长度<指针访问长度时触发异常
  40. 0xffff8000803b7138 <+96>: ldp x29, x30, [sp], #16
  41. 0xffff8000803b713c <+100>: autiasp
  42. 0xffff8000803b7140 <+104>: ret

1、传入指针和长度后,指针操作范围计算shadow存放地址

ptr >> 3 + kasan_offset

2、从shadow存放地址取出shadow值,然后和访问长度比较(转换单byte范围)

3、比如这里测试用例是分配128, 访问128+2 位置, 转换成地址(char*)index就是0x81

  1. (gdb) x /30b 0xFFFF600000DCAE80
  2. 0xffff600000dcae80: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
  3. 0xffff600000dcae88: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
  4. 0xffff600000dcae90: [0xfc] 0xfc 0xfc 0xfc 0xfc 0xfc 0xfc 0xfc
  5. 0xffff600000dcae98: 0xfc 0xfc 0xfc 0xfc 0xfc 0xfc
  6. shadow值0xffff600000dcae80对应地址0xffff000006e57400
  7. shadow值0xffff600000dcae90就是0xffff000006e57400 + 0x81

4、读取的地址0xffff000006e57400 + 0x81:

要求这里的shadow值0~7, 但是实际是0xfc(KASAN_SLAB_REDZONE),所以触发 kasan_report

其他测试用例(由于实现原理类似,不逐一展开分析):

  1. 触发page 内存踩踏
  2. /dev # echo 1 > kasan_test
  3. [ 47.775781] pagealloc_oob_right ffff000004265000
  4. [ 47.776110] ==================================================================
  5. [ 47.777583] BUG: KASAN: use-after-free in kasan_testcase_write+0x3e0/0x4d8 [kasan_driver]
  6. [ 47.780457] Read of size 1 at addr ffff000004266000 by task sh/179
  7. [ 47.781456]
  8. [ 47.782662] CPU: 1 PID: 179 Comm: sh Tainted: G N 6.6.1-g3cba94c761ec-dirty #15
  9. [ 47.783727] Hardware name: linux,dummy-virt (DT)
  10. [ 47.784470] Call trace:
  11. [ 47.784783] dump_backtrace+0x90/0xe8
  12. [ 47.785203] show_stack+0x18/0x24
  13. [ 47.785515] dump_stack_lvl+0x48/0x60
  14. [ 47.785785] print_report+0xf8/0x5d8
  15. [ 47.786054] kasan_report+0xc4/0x108
  16. [ 47.786303] __asan_load1+0x60/0x6c
  17. [ 47.786806] kasan_testcase_write+0x3e0/0x4d8 [kasan_driver]
  18. [ 47.787390] vfs_write+0x158/0x45c
  19. [ 47.787656] ksys_write+0xd0/0x180
  20. [ 47.787884] __arm64_sys_write+0x44/0x58
  21. [ 47.788165] invoke_syscall+0x60/0x184
  22. [ 47.788442] el0_svc_common.constprop.0+0x78/0x13c
  23. [ 47.788761] do_el0_svc+0x30/0x40
  24. [ 47.789029] el0_svc+0x38/0x70
  25. [ 47.789214] el0t_64_sync_handler+0x120/0x12c
  26. [ 47.789417] el0t_64_sync+0x190/0x194
  27. [ 47.789708]
  28. [ 47.789900] The buggy address belongs to the physical page:
  29. [ 47.790263] page:(____ptrval____) refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x44266
  30. [ 47.790795] flags: 0x3fffc0000000000(node=0|zone=0|lastcpupid=0xffff)
  31. [ 47.791171] page_type: 0xffffffff()
  32. [ 47.791590] raw: 03fffc0000000000 fffffc00001099c8 ffff00006af4d758 0000000000000000
  33. [ 47.791876] raw: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000000
  34. [ 47.792185] page dumped because: kasan: bad access detected
  35. [ 47.792400]
  36. [ 47.792513] Memory state around the buggy address:
  37. [ 47.792842] ffff000004265f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  38. [ 47.793129] ffff000004265f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  39. [ 47.793394] >ffff000004266000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
  40. [ 47.793694] ^
  41. [ 47.793896] ffff000004266080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
  42. [ 47.794152] ffff000004266100: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
  43. [ 47.794554] ==================================================================
  44. [ 47.795106] Disabling lock debugging due to kernel taint
  45. [ 47.795450] kasan_test_case type 1
  46. 触发全局变量内存踩踏
  47. /dev # echo 2 > kasan_test
  48. [ 54.484659] ==================================================================
  49. [ 54.484983] BUG: KASAN: global-out-of-bounds in kasan_testcase_write+0x2c0/0x4d8 [kasan_driver]
  50. [ 54.485402] Read of size 4 at addr ffff80007dbf20a8 by task sh/179
  51. [ 54.485638]
  52. [ 54.485772] CPU: 1 PID: 179 Comm: sh Tainted: G B N 6.6.1-g3cba94c761ec-dirty #15
  53. [ 54.486069] Hardware name: linux,dummy-virt (DT)
  54. [ 54.486249] Call trace:
  55. [ 54.486380] dump_backtrace+0x90/0xe8
  56. [ 54.486575] show_stack+0x18/0x24
  57. [ 54.486744] dump_stack_lvl+0x48/0x60
  58. [ 54.486930] print_report+0x318/0x5d8
  59. [ 54.487113] kasan_report+0xc4/0x108
  60. [ 54.487293] __asan_load4+0x9c/0xb8
  61. [ 54.487473] kasan_testcase_write+0x2c0/0x4d8 [kasan_driver]
  62. [ 54.487754] vfs_write+0x158/0x45c
  63. [ 54.487937] ksys_write+0xd0/0x180
  64. [ 54.488108] __arm64_sys_write+0x44/0x58
  65. [ 54.488294] invoke_syscall+0x60/0x184
  66. [ 54.488484] el0_svc_common.constprop.0+0x78/0x13c
  67. [ 54.488698] do_el0_svc+0x30/0x40
  68. [ 54.488876] el0_svc+0x38/0x70
  69. [ 54.489044] el0t_64_sync_handler+0x120/0x12c
  70. [ 54.489244] el0t_64_sync+0x190/0x194
  71. [ 54.489431]
  72. [ 54.489583] The buggy address belongs to the variable:
  73. [ 54.489776] global_kasan_value+0x8/0xffffffffffffef60 [kasan_driver]
  74. [ 54.490085]
  75. [ 54.490190] Memory state around the buggy address:
  76. [ 54.490382] ffff80007dbf1f80: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
  77. [ 54.490637] ffff80007dbf2000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f9 f9
  78. [ 54.490893] >ffff80007dbf2080: f9 f9 f9 f9 00 f9 f9 f9 f9 f9 f9 f9 00 00 00 00
  79. [ 54.491166] ^
  80. [ 54.491356] ffff80007dbf2100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  81. [ 54.491609] ffff80007dbf2180: 00 00 00 00 00 00 00 00 00 00 00 00 00 f9 f9 f9
  82. [ 54.491856] ==================================================================
  83. [ 54.492485] global arr oob access 0
  84. [ 54.492722] kasan_test_case type 2
  85. 触发stack内存踩踏
  86. /dev # echo 3 > kasan_test
  87. [ 75.450592] ==================================================================
  88. [ 75.452056] BUG: KASAN: stack-out-of-bounds in kasan_testcase_write+0x414/0x4d8 [kasan_driver]
  89. [ 75.454159] Read of size 1 at addr ffff8000873b7b1e by task sh/179
  90. [ 75.455514]
  91. [ 75.456157] CPU: 1 PID: 179 Comm: sh Tainted: G B N 6.6.1-g3cba94c761ec-dirty #15
  92. [ 75.457488] Hardware name: linux,dummy-virt (DT)
  93. [ 75.458119] Call trace:
  94. [ 75.458581] dump_backtrace+0x90/0xe8
  95. [ 75.459060] show_stack+0x18/0x24
  96. [ 75.459488] dump_stack_lvl+0x48/0x60
  97. [ 75.459950] print_report+0x318/0x5d8
  98. [ 75.460412] kasan_report+0xc4/0x108
  99. [ 75.460872] __asan_load1+0x60/0x6c
  100. [ 75.461068] kasan_testcase_write+0x414/0x4d8 [kasan_driver]
  101. [ 75.461358] vfs_write+0x158/0x45c
  102. [ 75.461550] ksys_write+0xd0/0x180
  103. [ 75.461719] __arm64_sys_write+0x44/0x58
  104. [ 75.461904] invoke_syscall+0x60/0x184
  105. [ 75.462092] el0_svc_common.constprop.0+0x78/0x13c
  106. [ 75.462328] do_el0_svc+0x30/0x40
  107. [ 75.462500] el0_svc+0x38/0x70
  108. [ 75.462816] el0t_64_sync_handler+0x120/0x12c
  109. [ 75.463091] el0t_64_sync+0x190/0x194
  110. [ 75.463336]
  111. [ 75.463560] The buggy address belongs to stack of task sh/179
  112. [ 75.463929] and is located at offset 142 in frame:
  113. [ 75.464205] kasan_testcase_write+0x0/0x4d8 [kasan_driver]
  114. [ 75.464666]
  115. [ 75.464913] This frame has 4 objects:
  116. [ 75.465338] [48, 52) 'i'
  117. [ 75.465413] [64, 72) 'array'
  118. [ 75.465635] [96, 104) 'array'
  119. [ 75.465813] [128, 138) 'stack_array'
  120. [ 75.465977]
  121. [ 75.466241] The buggy address belongs to the virtual mapping at
  122. [ 75.466241] [ffff8000873b0000, ffff8000873b9000) created by:
  123. [ 75.466241] kernel_clone+0xb4/0x470
  124. [ 75.466756]
  125. [ 75.466968] The buggy address belongs to the physical page:
  126. [ 75.467185] page:(____ptrval____) refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x4ca7c
  127. [ 75.467501] flags: 0x3fffc0000000000(node=0|zone=0|lastcpupid=0xffff)
  128. [ 75.467743] page_type: 0xffffffff()
  129. [ 75.467923] raw: 03fffc0000000000 0000000000000000 dead000000000122 0000000000000000
  130. [ 75.468199] raw: 0000000000000000 0000000000000000 00000001ffffffff 0000000000000000
  131. [ 75.468459] page dumped because: kasan: bad access detected
  132. [ 75.468660]
  133. [ 75.468764] Memory state around the buggy address:
  134. [ 75.468955] ffff8000873b7a00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  135. [ 75.469214] ffff8000873b7a80: 00 00 f1 f1 f1 f1 f1 f1 04 f2 00 f2 f2 f2 00 f2
  136. [ 75.469478] >ffff8000873b7b00: f2 f2 00 02 f3 f3 00 00 00 00 00 00 00 00 00 00
  137. [ 75.469725] ^
  138. [ 75.469903] ffff8000873b7b80: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00 00 00 00
  139. [ 75.470158] ffff8000873b7c00: 00 f2 f2 f2 f2 f2 00 00 00 00 00 00 f3 f3 f3 f3
  140. [ 75.470407] ==================================================================
  141. [ 75.470858] kasan_stack_oob 0
  142. [ 75.471036] kasan_test_case type 3
  143. 触发page use after free
  144. /dev # echo 4 > kasan_test
  145. [ 80.572006] pagealloc_uaf ffff000004265000
  146. [ 80.572276] ==================================================================
  147. [ 80.573408] BUG: KASAN: use-after-free in kasan_testcase_write+0x288/0x4d8 [kasan_driver]
  148. [ 80.574439] Read of size 1 at addr ffff000004265000 by task sh/179
  149. [ 80.575262]
  150. [ 80.575562] CPU: 1 PID: 179 Comm: sh Tainted: G B N 6.6.1-g3cba94c761ec-dirty #15
  151. [ 80.576651] Hardware name: linux,dummy-virt (DT)
  152. [ 80.577286] Call trace:
  153. [ 80.577887] dump_backtrace+0x90/0xe8
  154. [ 80.578659] show_stack+0x18/0x24
  155. [ 80.579220] dump_stack_lvl+0x48/0x60
  156. [ 80.579548] print_report+0xf8/0x5d8
  157. [ 80.579839] kasan_report+0xc4/0x108
  158. [ 80.580055] __asan_load1+0x60/0x6c
  159. [ 80.580236] kasan_testcase_write+0x288/0x4d8 [kasan_driver]
  160. [ 80.580523] vfs_write+0x158/0x45c
  161. [ 80.580706] ksys_write+0xd0/0x180
  162. [ 80.580887] __arm64_sys_write+0x44/0x58
  163. [ 80.581126] invoke_syscall+0x60/0x184
  164. [ 80.581378] el0_svc_common.constprop.0+0x78/0x13c
  165. [ 80.581653] do_el0_svc+0x30/0x40
  166. [ 80.581893] el0_svc+0x38/0x70
  167. [ 80.582130] el0t_64_sync_handler+0x120/0x12c
  168. [ 80.582425] el0t_64_sync+0x190/0x194
  169. [ 80.582701]
  170. [ 80.582861] The buggy address belongs to the physical page:
  171. [ 80.583170] page:(____ptrval____) refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x44265
  172. [ 80.583687] flags: 0x3fffc0000000000(node=0|zone=0|lastcpupid=0xffff)
  173. [ 80.584071] page_type: 0xffffffff()
  174. [ 80.584354] raw: 03fffc0000000000 fffffc0000109988 ffff00006af4d758 0000000000000000
  175. [ 80.584774] raw: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000000
  176. [ 80.585195] page dumped because: kasan: bad access detected
  177. [ 80.585532]
  178. [ 80.585697] Memory state around the buggy address:
  179. [ 80.586005] ffff000004264f00: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
  180. [ 80.586408] ffff000004264f80: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
  181. [ 80.586783] >ffff000004265000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
  182. [ 80.587041] ^
  183. [ 80.587203] ffff000004265080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
  184. [ 80.587465] ffff000004265100: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
  185. [ 80.587716] ==================================================================
  186. [ 80.588370] pagealloc_uaf 204
  187. [ 80.588569] kasan_test_case type 4
  188. 触发vmalloc内存踩踏
  189. /dev # echo 5 > kasan_test
  190. [ 86.262697] vmalloc_oob ffff800085bf5000
  191. [ 86.262824] ==================================================================
  192. [ 86.263246] BUG: KASAN: vmalloc-out-of-bounds in kasan_testcase_write+0x47c/0x4d8 [kasan_driver]
  193. [ 86.263603] Read of size 1 at addr ffff800085bf5800 by task sh/179
  194. [ 86.263816]
  195. [ 86.263932] CPU: 5 PID: 179 Comm: sh Tainted: G B N 6.6.1-g3cba94c761ec-dirty #15
  196. [ 86.264229] Hardware name: linux,dummy-virt (DT)
  197. [ 86.264395] Call trace:
  198. [ 86.264525] dump_backtrace+0x90/0xe8
  199. [ 86.264706] show_stack+0x18/0x24
  200. [ 86.264860] dump_stack_lvl+0x48/0x60
  201. [ 86.265059] print_report+0x318/0x5d8
  202. [ 86.265250] kasan_report+0xc4/0x108
  203. [ 86.265434] __asan_load1+0x60/0x6c
  204. [ 86.265627] kasan_testcase_write+0x47c/0x4d8 [kasan_driver]
  205. [ 86.265921] vfs_write+0x158/0x45c
  206. [ 86.266113] ksys_write+0xd0/0x180
  207. [ 86.266287] __arm64_sys_write+0x44/0x58
  208. [ 86.266476] invoke_syscall+0x60/0x184
  209. [ 86.266672] el0_svc_common.constprop.0+0x78/0x13c
  210. [ 86.266892] do_el0_svc+0x30/0x40
  211. [ 86.267078] el0_svc+0x38/0x70
  212. [ 86.267251] el0t_64_sync_handler+0x120/0x12c
  213. [ 86.267456] el0t_64_sync+0x190/0x194
  214. [ 86.267640]
  215. [ 86.267757] The buggy address belongs to the virtual mapping at
  216. [ 86.267757] [ffff800085bf5000, ffff800085bf7000) created by:
  217. [ 86.267757] kasan_testcase_write+0x444/0x4d8 [kasan_driver]
  218. [ 86.268317]
  219. [ 86.268428] The buggy address belongs to the physical page:
  220. [ 86.268644] page:(____ptrval____) refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x4cd8a
  221. [ 86.268963] flags: 0x3fffc0000000000(node=0|zone=0|lastcpupid=0xffff)
  222. [ 86.269271] page_type: 0xffffffff()
  223. [ 86.269461] raw: 03fffc0000000000 0000000000000000 dead000000000122 0000000000000000
  224. [ 86.269746] raw: 0000000000000000 0000000000000000 00000001ffffffff 0000000000000000
  225. [ 86.270008] page dumped because: kasan: bad access detected
  226. [ 86.270209]
  227. [ 86.270316] Memory state around the buggy address:
  228. [ 86.270511] ffff800085bf5700: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  229. [ 86.270771] ffff800085bf5780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  230. [ 86.271035] >ffff800085bf5800: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
  231. [ 86.271289] ^
  232. [ 86.271470] ffff800085bf5880: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
  233. [ 86.271748] ffff800085bf5900: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
  234. [ 86.271997] ==================================================================
  235. [ 86.272484] vmalloc_oob 0
  236. [ 86.272779] kasan_test_case type 5

六、小结

kasan 的核心思想是简单的,复杂主要体现在编译器插桩实现(好在gcc/clang都已经支持了),在所有分配的部分需要完成shadow的存储,所有读写的地方加入指针访问地址长度的shadow值检查。我们业务使用是比较简单的,工程应用上需要注意的一些点就是:

1、打开kasan后kenerl会变大,需要考虑boot分区的大小限制(预先需要足够)

2、bootloader引导时也需要注意物理地址划分,以前也遇到过将后面rootfs(ramdisk)覆盖导致无法启动的情况

3、默认是kasan report只是内核打印一次(后续触发也不会上报),大量机器测试时需要人力或者自动化脚本检查,出现问题我们想看下上下文或者一些变量状态也不方便,实际业务中通常增加 cmdline: kasan.fault=panic,这样发生问题时能保存现场,测试/开发同事也能第一时间发现并分析。

参考资料:

KASAN实现原理

HWAddress Sanitizer | Android NDK | Android Developers

Arm Memory Tagging Extension (MTE) | Android NDK | Android Developers

Address Sanitizer | Android NDK | Android Developers

https://developer.android.google.cn/ndk/guides/memory-debug?hl=zh-cn

Kernel page table dump

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/160003
推荐阅读
相关标签
  

闽ICP备14008679号