当前位置: 首页 > news >正文

网站APP注册做任务程序员公司

网站APP注册做任务,程序员公司,深圳品牌设计网站,企业网站首页怎么优化昨天的文章发出来后#xff0c;有同学在群里说内核也可以使用这个工具#xff0c;所以再转发一篇wowo网站的文章#xff0c;希望对大家有帮助。Linux 应用调试神器- ASan1. 前言KASAN是一个动态检测内存错误的工具。KASAN可以检测全局变量、栈、堆分配的内存发生越界访问等问… 昨天的文章发出来后有同学在群里说内核也可以使用这个工具所以再转发一篇wowo网站的文章希望对大家有帮助。Linux 应用调试神器- ASan1. 前言KASAN是一个动态检测内存错误的工具。KASAN可以检测全局变量、栈、堆分配的内存发生越界访问等问题。功能比SLUB DEBUG齐全并且支持实时检测。越界访问的严重性和危害性通过我之前的文章SLUB DEBUG技术应该有所了解。正是由于SLUB DEBUG缺陷因此我们需要一种更加强大的检测工具。难道你不想吗KASAN就是其中一种。KASAN的使用真的很简单。但是我是一个追求刨根问底的人。仅仅止步于使用的层面我是不愿意的只有更清楚的了解实现原理才能更加熟练的使用工具。不止是KASAN其他方面我也是这么认为。但是说实话写这篇文章是有点底气不足的。因为从我查阅的资料来说国内没有一篇文章说KASAN的工作原理国外也是没有什么文章关注KASAN的原理。大家好像都在说How to use。由于本人水平有限就根据现有的资料以及自己阅读代码揣摩其中的意思。本文章作为抛准引玉如果有不合理的地方还请指正。注文章代码分析基于linux-4.15.0-rc3。2. 简介KernelAddressSANitizerKASAN是一个动态检测内存错误的工具。它为找到use-after-free和out-of-bounds问题提供了一个快速和全面的解决方案。KASAN使用编译时检测每个内存访问因此您需要GCC 4.9.2或更高版本。检测堆栈或全局变量的越界访问需要GCC 5.0或更高版本。目前KASAN仅支持x86_64和arm64架构linux 4.4版本合入。你使用ARM64架构那么就需要保证linux版本在4.4以上。当然了如果你使用的linux也有可能打过KASAN的补丁。例如使用高通平台做手机的厂商使用linux 3.18同样支持KASAN。3. 如何使用使用KASAN工具是比较简单的只需要添加kernel以下配置项。CONFIG_SLUB_DEBUGyCONFIG_KASANy为什么这里必须打开SLUB_DEBUG呢是因为有段时间KASAN是依赖SLUBU_DEBUG的什么意思呢就是在Kconfig中使用了depends on明白了吧。不过最新的代码已经不需要依赖了可以看下提交。但是我建议你打开该选项因为log可以输出更多有用的信息。重新编译kernel即可编译之后你会发现boot.imgAndroid环境大小大了一倍左右。所以说影响效率不是没有道理的。不过我们可以作为产品发布前的最后检查也可以排查越界访问等问题。我们可以查看内核日志内容是否包含KASAN检查出的bugs信息。4. KASAN是如何实现检测的KASAN的原理是利用额外的内存标记可用内存的状态。这部分额外的内存被称作shadow memory影子区。KASAN将1/8的内存用作shadow memory。使用特殊的magic num填充shadow memory在每一次load/storeload/store检查指令由编译器插入内存的时候检测对应的shadow memory确定操作是否valid。连续8 bytes内存8 bytes align使用1 byte shadow memory标记。如果8 bytes内存都可以访问则shadow memory的值为0如果连续N(1 N 7) bytes可以访问则shadow memory的值为N如果8 bytes内存访问都是invalid则shadow memory的值为负数。 在代码运行时每一次memory access都会检测对应的shawdow memory的值是否valid。这就需要编译器为我们做些工作。编译的时候在每一次memory access前编译器会帮我们插入__asan_load##size()或者__asan_store##size()函数调用size是访问内存字节的数量。这也是要求更新版本gcc的原因只有更新的版本才支持自动插入。mov x0, #0x5678movk x0, #0x1234, lsl #16movk x0, #0x8000, lsl #32movk x0, #0xffff, lsl #48mov w1, #0x5bl __asan_store1strb w1, [x0]上面一段汇编指令是往0xffff800012345678地址写5。在KASAN打开的情况下编译器会帮我们自动插入bl __asan_store1指令__asan_store1函数就是检测一个地址对应的shadow memory的值是否允许写1 byte。蓝色汇编指令就是真正的内存访问。因此KASAN可以在out-of-bounds的时候及时检测。__asan_load##size()和__asan_store##size()的代码在mm/kasan/kasan.c文件实现。4.1. 如何根据shadow memory的值判断内存访问操作是否validshadow memory检测原理的实现主要就是__asan_load##size()和__asan_store##size()函数的实现。那么KASAN是如何根据访问的address以及对应的shadow memory的状态值来判断访问是否合法呢首先看一种最简单的情况。访问8 bytes内存。long *addr (long *)0xffff800012345678;*addr 0;以上代码是访问8 bytes情况检测原理如下long *addr (long *)0xffff800012345678;char *shadow (char *)(((unsigned long)addr 3) KASAN_SHADOW_OFFSE);if (*shadow)    report_bug();*addr 0;红色区域类似是编译器插入的指令。既然是访问8 bytes必须要保证对应的shadow mempry的值必须是0否则肯定是有问题。那么如果访问的是1,2 or 4 bytes该如何检查呢也很简单我们只需要修改一下if判断条件即可。修改如下if (*shadow *shadow ((unsigned long)addr 7) N); //N 1,2,4如果*shadow的值为0代表8 bytes均可以访问自然就不需要report bug。addr 7是计算访问地址相对于8字节对齐地址的偏移。还是使用下图来说明关系吧。假设内存是从地址8~15一共8 bytes。对应的shadow memory值为5现在访问11地址。那么这里的N只要大于2就是invalid。 4.2. shadow memory内存如何分配在ARM64中假设VA_BITS配置成48。那么kernel space空间大小是256TB因此shadow memory的内存需要32TB。我们需要在虚拟地址空间为KASAN shadow memory分配地址空间。所以我们有必要了解一下ARM64 memory layout。基于linux-4.15.0-rc3的代码分析我绘制了如下memory layoutVA_BITS 48。kernel space起始虚拟地址是0xffff_0000_0000_0000kernel space被分成几个部分分别是KASAN、MODULE、VMALLOC、FIXMAP、PCI_IO、VMEMMAP以及linear mapping。其中KASAN的大小是32TB正好是kernel space大小的1/8。不知道你注意到没有KERNEL的位置相对以前是不是有所不一样。你的印象中KERNEL是不是位于linear mapping区域这里怎么变成了VMALLOC区域这里是Ard Biesheuvel提交的修改。主要是为了迎接ARM64世界的KASLRwhich allows the kernel image to be located anywhere in the vmalloc area的到来。 4.3. 如何建立shadow memory的映射关系当打开KASAN的时候KASAN区域位于kernel space首地址处从0xffff_0000_0000_0000地址开始大小是32TB。shadow memory和kernel address转换关系是shadow_addr (kaddr 3)  KASAN_SHADOW_OFFSE。为了将[0xffff_0000_0000_0000, 0xffff_ffff_ffff_ffff]和[0xffff_0000_0000_0000, 0xffff_1fff_ffff_ffff]对应起来因此计算KASAN_SHADOW_OFFSE的值为0xdfff_2000_0000_0000。我们将KASAN区域放大如下图所示。 KASAN区域仅仅是分配的虚拟地址在访问的时候必须建立和物理地址的映射才可以访问。上图就是KASAN建立的映射布局。左边是系统启动初期建立的映射。在kasan_early_init()函数中将所有的KASAN区域映射到kasan_zero_page物理页面。因此系统启动初期KASAN并不能工作。右侧是在kasan_init()函数中建立的映射关系kasan_init()函数执行结束就预示着KASAN的正常工作。我们将不需要address sanitizer功能的区域同样还是映射到kasan_zero_page物理页面并且是readonly。我们主要是检测kernel和物理内存是否存在UAF或者OOB问题。所以建立KERNEL和linear mapping仅仅是所有的物理地址建立的映射区域区域对应的shadow memory建立真实的映射关系。MOUDLE区域对应的shadow memory的映射关系也是需要创建的但是映射关系建立是动态的他在module加载的时候才会去创建映射关系。4.4. 伙伴系统分配的内存的shadow memory值如何填充既然shadow memory已经建立映射接下来的事情就是探究各种内存分配器向shadow memory填充什么数据了。首先看一下伙伴系统allocate page(s)函数填充shadow memory情况。 假设我们从buddy system分配4 pages。系统首先从order2的链表中摘下一块内存然后根据shadow memory address和memory address之间的对应的关系找对应的shadow memory。这里shadow memory的大小将会是2KB系统会全部填充0代表内存可以访问。我们对分配的内存的任意地址内存进行访问的时候首先都会找到对应的shadow memory然后根据shadow memory value判断访问内存操作是否valid。如果释放pages情况又是如何呢 同样的当释放pages的时候会填充shadow memory的值为0xFF。如果释放之后依然访问内存的话此时KASAN根据shadow memory的值是0xFF就可以断这是一个use-after-free问题。4.5. SLUB分配对象的内存的shadow memory值如何填充当我们打开KASAN的时候SLUB Allocator管理的object layout将会放生一定的变化。如下图所示。 在打开SLUB_DEBUG的时候object就增加很多内存KASAN打开之后在此基础上又加了一截。为什么这里必须打开SLUB_DEBUG呢是因为有段时间KASAN是依赖SLUBU_DEBUG的什么意思呢就是在Kconfig中使用了depends on明白了吧。不过最新的代码已经不需要依赖了可以看下提交。当我们第一次创建slab缓存池的时候系统会调用kasan_poison_slab()函数初始化shadow memory为下图的模样。整个slab对应的shadow memory都填充0xFC。 上述步骤虽然填充了0xFC但是接下来初始化object的时候会改变一些shadow memory的值。我们先看一下kmalloc(20)的情况。我们知道kmalloc()就是基于SLUB Allocator实现的所以会从kmalloc-32的kmem_cache中分配一个32 bytes object。 首先调用kmalloc(20)函数会匹配到kmalloc-32的kmem_cache因此实际分配的object大小是32 bytes。KASAN同样会标记剩下的12 bytes的shadow memory为不可访问状态。根据object的地址计算shadow memory的地址并开始填充数值。由于kmalloc()返回的object的size是32 bytes由于kmalloc(20)只申请了20 bytes剩下的12 bytes不能使用。KASAN必须标记shadow memory这种情况。object对应的4 bytes shadow memory分别填充00 00 04 FC。00代表8个连续的字节可以访问。04代表前4个字节可以访问。作为越界访问的检测的方法。总共加在一起是正好是20 bytes可访问。0xFC是Redzone标记。如果访问了Redzone区域KASAN就会检测out-of-bounds的发生。当申请使用之后现在调用kfree()释放之后的shadow memory情况是怎样的呢看下图。 根据object首地址找到对应的shadow memory32 bytes object对应4 bytes的shadow memory现在填充0xFB标记内存是释放的状态。此时如果继续访问object那么根据shadow memory的状态值既可以确定是use-after-free问题。4.6. 全局变量的shadow memory值如何填充前面的分析都是基于内存分配器的Redzone都会随着内存分配器一起分配。那么global variables如何检测呢global variable的Redzone在哪里呢这就需要编译器下手了。编译器会帮我们填充Redzone区域。例如我们定义一个全局变量a编译器会帮我们填充成下面的样子。char a[4];转换struct{ char original[4]; char redzone[60]; } a;//32 bytes aligned如果这里你问我为什么填充60 bytes。其实我也不知道。这个转换例子也是从KASAN作者的PPT中拿过来的。估计要涉及编译器相关的知识我无能为力了但是下面做实验来猜吧。当然了PPT的内容也需要验证才具有说服力。尽信书则不如无书。我特地写三个全局变量来验证。发现System.map分配地址之间的差值正好是0x40。因此这里的确是填充60 bytes。另外从我的测试发现如果上述的数组a的大小是33的时候填充的redzone就是63 bytes。所以我推测填充的原理是这样的。全局变量实际占用内存总数S以byte为单位按照每块32 bytes平均分成N块。假设最后一块内存距离目标32 bytes还差y bytesif S%32 0y 0那么redzone填充的大小就是(y 32) bytes。画图示意如下S%32 ! 0。因此总结的规律是redzone 63 – (S - 1) % 32。 全局变量redzone区域对应的shadow memory是在什么填充的呢又是如何调用的呢这部分是由编译器帮我们完成的。编译器会为每一个全局变量创建一个函数函数名称是_GLOBAL__sub_I_65535_1_##global_variable_name。这个函数中通过调用__asan_register_globals()函数完成shadow memory标记。并且将自动生成的这个函数的首地址放在.init_array段。在kernel启动阶段通过以下代调用关系最终调用所有全局变量的构造函数。kernel_init_freeable()-do_basic_setup() -do_ctors()。do_ctors()代码实现如下staticvoid __init do_ctors(void) { ctor_fn_t*fn (ctor_fn_t*) __ctors_start; for(; fn (ctor_fn_t*) __ctors_end; fn) (*fn)(); }这里的代码意思对于轻车熟路的你再熟悉不过了吧。因为内核中这么搞的太多了。便利__ctors_start和__ctors_end之间的所有数据作为函数地址进行调用即完成了所有的global variables的shadow memory初始化。我们可以从链接脚本中知道__ctors_start和__ctors_end的意思。#define KERNEL_CTORS()  . ALIGN(8);              \            VMLINUX_SYMBOL(__ctors_start) .; \            KEEP(*(.ctors))            \            KEEP(*(SORT(.init_array.*)))       \            KEEP(*(.init_array))           \            VMLINUX_SYMBOL(__ctors_end) .;上面说了这么多不知道你是否产生了疑心怎么都是猜啊猜的能准确吗是的我也这么觉得。是骡子是马拉出来溜溜呗现在用事实说话。首先我创建一个c文件drivers/input/smc.c。在smc.c文件中创建3个全局变量如下 然后就随便使用吧编译kernel我们先看看System.map文件中3个全局变量分配的地址。ffff200009f540e0 B smc_num1ffff200009f54120 B smc_num2ffff200009f54160 B smc_num3还记得上面说会有一个形如_GLOBAL__sub_I_65535_1_##global_variable_name的函数吗在System.map文件文件中我看到了_GLOBAL__sub_I_65535_1_smc_num1符号。但是没有smc_num2和smc_num3的构造函数。你是不是很奇怪不是每一个全局变量都会创建一个类似的构造函数吗马上为你揭晓。我们先执行aarch64-linux-gnu-objdump –s –x –d vmlinux vmlinux.txt命令得到反编译文件。现在好多重要的信息在vmlinux.txt。现在主要就是查看vmlinux.txt文件。先看一下_GLOBAL__sub_I_65535_1_smc_num1函数的实现。ffff200009381df0 _GLOBAL__sub_I_65535_1_smc_num1:ffff200009381df0:   a9bf7bfd    stp x29, x30, [sp,#-16]!ffff200009381df4:   b0001800    adrp    x0, ffff200009682000ffff200009381df8:   91308000    add x0, x0, #0xc20ffff200009381dfc:   d2800061    mov x1, #0x3                    // #3ffff200009381e00:   910003fd    mov x29, spffff200009381e04:   9100c000    add x0, x0, #0x30ffff200009381e08:   97c09fb8    bl  ffff2000083a9ce8 __asan_register_globalsffff200009381e0c:   a8c17bfd    ldp x29, x30, [sp],#16ffff200009381e10:   d65f03c0    ret汇编和C语言传递参数在ARM64平台使用的是x0~x7。通过上面的汇编计算一下x00xffff200009682c50x13。然后调用__asan_register_globals()函数x0和x1就是传递的参数。我们看一下__asan_register_globals()函数实现。void __asan_register_globals(struct kasan_global *globals,size_t size) { int i; for(i 0; i size; i)register_global(globals[i]); }size是3就是要初始化全局变量的个数所以这里只需要一个构造函数即可。一次性将3个全局变量全部搞定。这里再说一点猜测吧我猜测是以文件为单位编译器创建一个构造函数即可将本文件全局变量一次性全部打包初始化。第一个参数globals是0xffff200009682c50继续从vmlinux.txt中查看该地址处的数据。struct kasan_global是编译器帮我们自动创建的结构体每一个全局变量对应一个struct kasan_global结构体。struct kasan_global结构体存放的位置是.data段因此我们可以从.data段查找当前地址对应的数据。数据如下ffff200009682c50 6041f509 0020ffff 07000000 00000000ffff200009682c60 40000000 00000000 d0d62b09 0020ffffffff200009682c70 b8d62b09 0020ffff 00000000 00000000ffff200009682c80 202c6809 0020ffff 2041f509 0020ffffffff200009682c90 1f000000 00000000 40000000 00000000ffff200009682ca0 e0d62b09 0020ffff b8d62b09 0020ffffffff200009682cb0 00000000 00000000 302c6809 0020ffffffff200009682cc0 e040f509 0020ffff 04000000 00000000ffff200009682cd0 40000000 00000000 f0d62b09 0020ffffffff200009682ce0 b8d62b09 0020ffff 00000000 00000000首先ffff200009682c50对应的第一个数据6041f509 0020ffff这是个啥其实是一个地址数据你是不是又疑问了ARM64的kernel space地址不是ffff开头吗这个怎么60开头其实这个地址数据是反过来的你应该从右向左看。这个地址其实是ffff200009f54160。这不正是smc_num3的地址嘛解析这段数据之前需要了解一下struct kasan_global结构体。/* The layout of struct dictated by compiler */ struct kasan_global { constvoid*beg;/* Address of the beginning of the global variable. */ size_t size;/* Size of the global variable. */ size_t size_with_redzone;/* Size of the variable size of the red zone. 32 bytes aligned */ constvoid*name; constvoid*module_name;/* Name of the module where the global variable is declared. */ unsignedlong has_dynamic_init;/* This needed for C */ #if KASAN_ABI_VERSION 4 struct kasan_source_location *location; #endif };第一个成员beg就是全局变量的首地址。跟上面的分析一致。第二个成员size从上面数据看出是7正好对应我们定义的smc_num3[7]正好7 bytes。size_with_redzone的值是0x40正好是64。根据上面猜测redzone63-(7-1)%3257。加上size正好是64说明之前猜测的redzone计算方法没错。name成员对应的地址是ffff2000092bd6d0。看下ffff2000092bd6d0存储的是什么。ffff2000092bd6d0 736d635f 6e756d33 00000000 00000000  smc_num3........所以name就是全局变量的名称转换成字符串。同样的方式得到module_name的地址是ffff2000092bd6b8。继续看看这段地址存储的数据。ffff2000092bd6b0 65000000 00000000 64726976 6572732f  e.......drivers/ffff2000092bd6c0 696e7075 742f736d 632e6300 00000000  input/smc.c.....一目了然module_name是文件的路径。has_dynamic_init的值就是0这是C需要的。我用的GCC版本是5.0左右所以这里的KASAN_ABI_VERSION4。这里location成员的地址是ffff200009682c20继续追踪该地址的数据。ffff200009682c20 b8d62b09 0020ffff 0e000000 0f000000解析这段数据之前要先了解struct kasan_source_location结构体。/* The layout of struct dictated by compiler */ struct kasan_source_location { constchar*filename; int line_no; int column_no; };第一个成员filename地址是ffff2000092bd6b8和module_name一样的数据。剩下两个数据分别是14和15分别代表全局变量定义地方的行号和列号。现在回到上面我定义变量的截图仔细数数列号是不是15行号截图中也有哦特地截出来给你看的。剩下的struct kasan_global数据就是smc_num1和smc_num2的数据。分析就不说了。前面说_GLOBAL__sub_I_65535_1_smc_num1函数会被自动调用该地址数据填充在__ctors_start和__ctors_end之间。现在也证明一下观点。先从System.map得到符号的地址数据。ffff2000093ac5d8 T __ctors_startffff2000093ae860 T __ctors_end然后搜索一下_GLOBAL__sub_I_65535_1_smc_num1的地址ffff200009381df0被存储在什么位置记得搜索的关键字是f01d3809 0020ffff。ffff2000093ae0c0 f01d3809 0020ffff 181e3809 0020ffff可以看出ffff2000093ae0c0地址处存储着_GLOBAL__sub_I_65535_1_smc_num1函数地址。这个地址不是正好位于__ctors_start和__ctors_end之间嘛现在就剩下__asan_register_globals()函数到底是是怎么初始化shadow memory的呢以char a[4]为例如下图所示。 a[4]只有4 bytes可以访问所以对应的shadow memory的第一个byte值是4后面的redzone就填充0xFA作为越界检测。a[4]只有4 bytes可以访问所以对应的shadow memory的第一个byte值是4后面的redzone就填充0xFA作为越界检测。因为这里是全局变量因此分配的内存区域位于kernel区域。4.7. 栈分配变量的readzone是如何分配的从栈中分配的变量同样和全局变量一样需要填充一些内存作为redzone区域。下面继续举个例子说明编译器怎么填充。首先来一段正常的代码没有编译器的插手。void foo() { char a[328]; }再来看看编译器插了哪些东西进去。void foo(){    char rz1[32];    char a[328];    char rz2[56];    int *shadow rz1 3 KASAN_SHADOW_OFFSE;    shadow[0] 0xffffffff;    shadow[11] 0xffffff00;    shadow[12] 0xffffffff;------------------------使用完毕----------------------------------------    shadow[0] shadow[11] shadow[12] 0;}红色部分是编译器填充内存rz2是56可以根据上一节全局变量的公式套用计算得到。但是这里在变量前面竟然还有32 bytes的rz1。这个是和全局变量的不同我猜测这里是为了检测栈变量左边界越界问题。蓝色部分代码也是编译器填充初始化shadow memory。栈的填充就没有探究那么深入了有兴趣的读者可以自己探究。5. Error log信息包含哪些信息从kernel的Documentation文档找份典型的KASAN bug输出的log信息如下。BUG: AddressSanitizer: out of bounds access in kmalloc_oob_right0x65/0x75 [test_kasan] at addr ffff8800693bc5d3Write of size 1 by task modprobe/1689BUG kmalloc-128 (Not tainted): kasan error-----------------------------------------------------------------------------Disabling lock debugging due to kernel taintINFO: Allocated in kmalloc_oob_right0x3d/0x75 [test_kasan] age0 cpu0 pid1689 __slab_alloc0x4b4/0x4f0 kmem_cache_alloc_trace0x10b/0x190 kmalloc_oob_right0x3d/0x75 [test_kasan] init_module0x9/0x47 [test_kasan] do_one_initcall0x99/0x200 load_module0x2cb3/0x3b20 SyS_finit_module0x76/0x80 system_call_fastpath0x12/0x17INFO: Slab 0xffffea0001a4ef00 objects17 used7 fp0xffff8800693bd728 flags0x100000000004080INFO: Object 0xffff8800693bc558 offset1368 fp0xffff8800693bc720Bytes b4 ffff8800693bc548: 00 00 00 00 00 00 00 00 5a 5a 5a 5a 5a 5a 5a 5a  ........ZZZZZZZZObject ffff8800693bc558: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b  kkkkkkkkkkkkkkkkObject ffff8800693bc568: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b  kkkkkkkkkkkkkkkkObject ffff8800693bc578: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b  kkkkkkkkkkkkkkkkObject ffff8800693bc588: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b  kkkkkkkkkkkkkkkkObject ffff8800693bc598: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b  kkkkkkkkkkkkkkkkObject ffff8800693bc5a8: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b  kkkkkkkkkkkkkkkkObject ffff8800693bc5b8: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b  kkkkkkkkkkkkkkkkObject ffff8800693bc5c8: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b a5  kkkkkkkkkkkkkkk.Redzone ffff8800693bc5d8: cc cc cc cc cc cc cc cc                          ........Padding ffff8800693bc718: 5a 5a 5a 5a 5a 5a 5a 5a                          ZZZZZZZZCPU: 0 PID: 1689 Comm: modprobe Tainted: G    B          3.18.0-rc1-mm1 #98Hardware name: QEMU Standard PC (i440FX PIIX, 1996), BIOS rel-1.7.5-0-ge51488c-20140602_164612-nilsson.home.kraxel.org 04/01/2014 ffff8800693bc000 0000000000000000 ffff8800693bc558 ffff88006923bb78 ffffffff81cc68ae 00000000000000f3 ffff88006d407600 ffff88006923bba8 ffffffff811fd848 ffff88006d407600 ffffea0001a4ef00 ffff8800693bc558Call Trace: [ffffffff81cc68ae] dump_stack0x46/0x58 [ffffffff811fd848] print_trailer0xf8/0x160 [ffffffffa00026a7] ? kmem_cache_oob0xc3/0xc3 [test_kasan] [ffffffff811ff0f5] object_err0x35/0x40 [ffffffffa0002065] ? kmalloc_oob_right0x65/0x75 [test_kasan] [ffffffff8120b9fa] kasan_report_error0x38a/0x3f0 [ffffffff8120a79f] ? kasan_poison_shadow0x2f/0x40 [ffffffff8120b344] ? kasan_unpoison_shadow0x14/0x40 [ffffffff8120a79f] ? kasan_poison_shadow0x2f/0x40 [ffffffffa00026a7] ? kmem_cache_oob0xc3/0xc3 [test_kasan] [ffffffff8120a995] __asan_store10x75/0xb0 [ffffffffa0002601] ? kmem_cache_oob0x1d/0xc3 [test_kasan] [ffffffffa0002065] ? kmalloc_oob_right0x65/0x75 [test_kasan] [ffffffffa0002065] kmalloc_oob_right0x65/0x75 [test_kasan] [ffffffffa00026b0] init_module0x9/0x47 [test_kasan] [ffffffff810002d9] do_one_initcall0x99/0x200 [ffffffff811e4e5c] ? __vunmap0xec/0x160 [ffffffff81114f63] load_module0x2cb3/0x3b20 [ffffffff8110fd70] ? m_show0x240/0x240 [ffffffff81115f06] SyS_finit_module0x76/0x80 [ffffffff81cd3129] system_call_fastpath0x12/0x17Memory state around the buggy address: ffff8800693bc300: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff8800693bc380: fc fc 00 00 00 00 00 00 00 00 00 00 00 00 00 fc ffff8800693bc400: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff8800693bc480: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff8800693bc500: fc fc fc fc fc fc fc fc fc fc fc 00 00 00 00 00ffff8800693bc580: 00 00 00 00 00 00 00 00 00 00 03 fc fc fc fc fc                                                                           ^ ffff8800693bc600: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff8800693bc680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff8800693bc700: fc fc fc fc fb fb fb fb fb fb fb fb fb fb fb fb ffff8800693bc780: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff8800693bc800: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb输出的信息很丰富包含了bug发生的类型、SLUB输出的object内存信息、Call Trace以及shadow memory的状态值。其中红色信息都是比较重要的信息。我没有写demo历程而是找了一份log信息不是我想偷懒而是锻炼自己。怎么锻炼呢我想问的是从这份log中你可以推测代码应该是怎么样的我可以得到一下信息1) 程序是通过kmalloc接口申请内存的2) 申请的内存大小是123 bytes即p kamlloc(123);3) 代码中类似往p[123]中写1 bytes导致越界访问的bug4) 在3)步骤发生前没有任何的对该内存的写操作如果你也能得到以上4点猜测我觉的我写的这几篇文章你是真的看明白了。首先输出信息是有SLUB的信息所以应该是通过kmalloc()接口在打印的shadow memory的值中我们看到连续的15个0和一个3所以申请的内存size就是15x83123由于是往ffff8800693bc5d3地址写1个字节并且object首地址是ffff8800693bc558所以推测是往p[123]写1 byte出问题由于log中将object中所有的128 bytes数据全部打印出来一共是127个0x6b和一个0xa5SLUB DEBUG文章介绍的内容。所以我推测在3)步骤发生前没有任何的对该内存的写操作。 6. 补充我看了linux-4.18的代码KASAN的log输出已经发生了部分变化。例如上面举例的SLUB的object的内容就不会打印了。我们用一下的程序展示这些变化实际上就是上面举例用的程序。static noinline void __init kmalloc_oob_right(void) { char*ptr; size_t size 123;ptr kmalloc(size, GFP_KERNEL); if(!ptr){pr_err(Allocation failed\n); return;}ptr[size]x;kfree(ptr); }针对以上代码KASAN检测到bug后的输出log如下 BUG: KASAN: slab-out-of-bounds in kmalloc_oob_right0x6c/0x8c Write of size 1 at addr ffffffc0cb114d7b by task swapper/0/1 CPU: 4 PID: 1 Comm: swapper/0 Tainted: G S W 4.9.82-perf #310 Hardware name:QualcommTechnologies,Inc. SDM632 PMI632 Call trace: [ffffff90cf88d9f8] dump_backtrace0x0/0x320 [ffffff90cf88dd2c] show_stack0x14/0x20 [ffffff90cfdd1148] dump_stack0xa8/0xd0 [ffffff90cfabf298] print_address_description0x60/0x250 [ffffff90cfabf6a0] kasan_report.part.20x218/0x2f0 [ffffff90cfabfac0] kasan_report0x20/0x28 [ffffff90cfabdc64] __asan_store10x4c/0x58 [ffffff90d1a4f760] kmalloc_oob_right0x6c/0x8c [ffffff90d1a50448] kmalloc_tests_init0xc/0x68 [ffffff90cf8845dc] do_one_initcall0xa4/0x1f0 [ffffff90d1a011ac] kernel_init_freeable0x244/0x300 [ffffff90d0d6da70] kernel_init0x10/0x110 [ffffff90cf8842a0] ret_from_fork0x10/0x30 Allocatedby task 1:kasan_kmalloc0xd8/0x188kmem_cache_alloc_trace0x130/0x248 kmalloc_oob_right0x4c/0x8ckmalloc_tests_init0xc/0x68do_one_initcall0xa4/0x1f0kernel_init_freeable0x244/0x300kernel_init0x10/0x110 ret_from_fork0x10/0x30 Freedby task 1:kasan_slab_free0x88/0x178kfree0x84/0x298 kobject_uevent_env0x144/0x620kobject_uevent0x10/0x18device_add0x5f8/0x860amba_device_try_add0x22c/0x2f8amba_device_add0x20/0x128 of_platform_bus_create0x390/0x478of_platform_bus_create0x21c/0x478of_platform_populate0x4c/0xb8of_platform_default_populate_init0x78/0x8cdo_one_initcall0xa4/0x1f0 kernel_init_freeable0x244/0x300kernel_init0x10/0x110ret_from_fork0x10/0x30 The buggy address belongs to the object at ffffffc0cb114d00 which belongs to the cache kmalloc-128 of size 128 The buggy address is located 123 bytes inside of 128-byte region [ffffffc0cb114d00, ffffffc0cb114d80) The buggy address belongs to the page: page:ffffffbf032c4500 count:1 mapcount:0 mapping:(null) index:0xffffffc0cb115200 compound_mapcount:0 flags: 0x4080(slab|head) page dumped because: kasan: bad access detected Memory state around the buggy address:ffffffc0cb114c00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffffffc0cb114c80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffffffc0cb114d00:00000000000000000000000000000003 ^ffffffc0cb114d80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fcffffffc0cb114e00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc 我们从上面的log可以分析如下数据line2发生越界访问位置。line3越界写1个字节写的地址是0xffffffc0cb114d7b。当前进程是comm是swapper/0pid是1。line7Call trace方便定位出问题的函数调用关系。line22该object分配的调用栈并指出分配内存的进程pid是1。line32释放该object的调用栈上次释放并指出释放内存的进程pid是1。line49指出slub相关的信息从“kmalloc-28”的kmem_cache分配的object。object起始地址是0xffffffc0cb114d00。line51访问出问题的地址位于object起始地址偏移123 bytes的位置。object的地址范围是[0xffffffc0cb114d00, 0xffffffc0cb114d80)。object实际大小是128 bytes。line61出问题地址对应的shadow memory的值可以确定申请内存的实际大小是123 bytes。参考文献1How to use KASAN to debug memory corruption in OpenStack environment.pdf2KernelAddressSanitizer (KASan) a fast memory error detector for the Linux kernel.pdf原创文章转发请注明出处。蜗窝科技www.wowotech.net。整理写代码的篮球球痴推荐阅读专辑|Linux文章汇总专辑|程序人生专辑|C语言我的知识小密圈关注公众号后台回复「1024」获取学习资料网盘链接。欢迎点赞关注转发在看您的每一次鼓励我都将铭记于心~嵌入式Linux微信扫描二维码关注我的公众号
http://www.zqtcl.cn/news/861657/

相关文章:

  • 重庆智能网站建设哪里好怎么在微信做企业网站
  • 甘肃建设局网站丰都网站建设公司
  • 四川建设设计公司网站网站建设好的图片
  • 建设旅游网站的总结做网站链接怎么做
  • 深圳网站建设建设wordpress cms 布局
  • 企业网站的建立必要性公司建网站哪家
  • 自己做的旅游网站 介绍免费的网站推广在线推广
  • 阿里巴巴 网站设计龙华建设网站公司
  • 番禺网站优化手机商城是什么意思
  • 如何做网站关键词wordpress安装卡死
  • word模板免费下载网站山东定制网站建设公司
  • 郑州网站推广排名公司win7上怎样卸载wordpress
  • 科技网站有哪些wordpress代码编辑器件
  • 做英文企业网站多钱钱wordpress调用外链图片
  • 自学网站查分数西双版纳傣族自治州天气
  • 网站建设一个多少钱wordpress朗读句子插件
  • 网站关键词怎么填写找代理商的渠道有哪些
  • 网站开发销售简历范文新建网站网络空间
  • 舟山外贸建站公司制作公司简介
  • 菜鸟是什么网站威海网站建设费用
  • 网站开发花费如何制作个人网页兼职
  • 网站鼠标特效用户体验最好的网站
  • 网站设计步骤图南通网站建设公司
  • 做盗版系统网站会不会开发次元世界
  • 下载爱南宁官方网站手机app开发软件有哪些
  • 云浮网站设计不收费的企业查询网站
  • 网站栏目怎么做iis网站筛选器被挂马
  • 网站开发中遇到的主要问题品牌营销策略包括哪些内容
  • 网站制作易捷网络十大社区团购平台有哪些
  • 哈尔滨口碑好的建站公司做网站制作一般多少钱