0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

基于SLUB的DEBUG功能,如何帮忙检测内存越界和访问已经释放的内存

Linux阅码场 2018-02-08 14:11 次阅读

本文简介:SLAB内存分配器-SLUB的DEBUG功能,如何帮忙检测内存越界(out-of-bounds)和访问已经释放的内存(use-after-free)。本文目录:

1. 前言

2. SLUB DEBUG功能

3. object layout

4. SLUB DEBUG原理

5. slabinfo

1. 前言

在工作中,经常会遇到由于越界导致的各种奇怪的问题。为什么越界访问导致的问题很奇怪呢?在工作差不多半年的时间里我就遇到了很多越界访问导致的问题(不得不吐槽下IC厂商提供的driver,总是隐藏着bug)。比如说越界访问导致的死机问题,这种问题的出现一般需要长时间测试才能发现,而且发现的时候即使有panic log。你也没什么头绪。这是为什么呢?假设驱动A通过kmalloc()申请了一段内存,不注意越界改写了与其相邻的object的数据(经过我之前一篇SLUB的文章分析,你应该明白kmalloc基于kmem_cache实现的),假设被改写的object是B驱动使用的,巧合B驱动使用object存储的是地址数据,如果B驱动访问这个地址。那么完了,B驱动死了,panic也是怪B驱动。试想一下,这块被改写的object是哪个驱动使用,是不是哪个驱动就倒霉了?并且每一次死机的log中panic极有可能发生在不同的模块。但是真正的元凶却是A驱动,他没事你还不知道,是不是很恐怖?简直是借刀杀人啊!

当然,越界访问也不一定会死机。之前就遇到一个很奇怪的问题。有两个全局数组变量(用作存储字符串)分别被模块C和D使用。这两个数组是上层需要显示的name信息。当C和D模块都工作的时候,发现C模块的name显示不对,但是D模块的name显示正常。将D模块remove,发现C模块的name显示正确。当时看了下System.map文件,发现这两个全局数组变量分配的内存是在一起的,由于D模块越界写导致的。而这种情况就不会死机。但是当你遇到这种情况的时候,你很惊讶,怎么会这样?两个模块之间根本就没关系啊!如果完全不借助检测工具去查找问题是相当费时间的。而且有可能还没什么头绪。

这种问题我们该怎么定位?因此我们遇到一种debug的手段,可以检测out-of-bounds(oob)问题。刚才的第一种情况就可以SLUB自带debug功能。针对第二种情况就需要借助更加强大的KASAN工具(后续会有文章介绍)。

因此,我们需要一种debug手段帮助我们定位问题。SLUB DEBUG就是其中的一种。但是SLUB DEBUG仅仅针对从slub分配器分配的内存,如果你需要检测从栈中或者数据区分配内存的问题,就不行了。当然了,你可以选择KASAN。本文主要关注SLUB DEBUG的原理,如何定位这些问题的。

SLUB DEBUG检测oob问题原理也很简单,既然为了发现是否越界,那么就在分配出去的内存尾部添加一段额外的内存,填充特殊数字(magic num)。我们只需要检测这块额外的内存的数据是否被修改就可以知道是否发生了oob情况。而这段额外的内存就叫做Redzone。直译过来“红色区域”是不是有种神圣不可侵犯的感觉。

说明:slab是最早加入linux的,在那时只有slab的存在。随着时间的推移slub出现了,slub是在slab基础上进行的改进,在大型机上表现出色。而slob是针对小型系统设计的。由于slub实现的接口和slab接口保持一致(虽然你用的是slub分配器,但是很多函数名称和数据结构还是依然和slab一致),所以有时候用slab来统称slab, slub和slob。slab, slub和slob仅仅是分配内存策略不同。管理的思想基本一致。本篇文章中说的是slub分配器debug原理。但是针对分配器管理的内存,下文统称为slab缓存池。所以文章中slub和slab会混用,表示同一个意思。

注:文章代码分析基于linux-4.15.0-rc3。

2.SLUB DEBUG功能

SLUB DEBUG可以检测内存越界(out-of-bounds)和访问已经释放的内存(use-after-free)等问题。

1.1. 如何打开功能

重新配置kernel选项,打开如下选项即可。

CONFIG_SLUB=y

CONFIG_SLUB_DEBUG=y

CONFIG_SLUB_DEBUG_ON=y

1.2. 如何使用

程序中的bug如果想用SLUB DEBUG去检测,还需要slabinfo命令。因为,SLUB内存检测功能在某些情况下不能立刻检测出来,必须主动触发,因此我们需要借助slabinfo命令触发SLUB allocator检测功能。和KASAN相比较而言,这也是SLUB DEBUG的一个劣势。毕竟KASAN可以做到在越界问题出现时就报出问题。

slabinfo工具源码位于tools/vm目录。可以使用如下命令编译slabinfo工具(针对ARM64 architecture)。

aarch64-linux-gnu-gcc -o slabinfo slabinfo.c

当系统开机之后,就可以运行slaninfo –v命令触发SLUB allocator检测所有的object,并将log信息输出到syslog。接下来的任务就是查看log信息是否包含SLUB allocator输出的bug log。其实有些bug是不需要运行slabinfo命令即可捕捉,但是有些却必须使用slabinfo –v命令才可以。下一节将会介绍SLUB DEBUG的原理,为你揭开哪些bug不需要slabinfo命令。

3. object layout

配置kernel选项CONFIG_SLUB_DEBUG_ON后,在创建kmem_cache的时候会传递很多flags(SLAB_CONSISTENCY_CHECKS、SLAB_RED_ZONE、SLAB_POISON、SLAB_STORE_USER)。针对这些flags,SLUB allocator管理的object对象的format将会发生变化。如下图所示。

基于SLUB的DEBUG功能,如何帮忙检测内存越界和访问已经释放的内存

SLUB DEBUG关闭的情况下,free pointer是内嵌在object之中的,但是SLUB DEBUG打开之后,free pointer是在object之外,并且多了很多其他的内存,例如red zone、trace和red_left_pad等。这里之所以将FP后移就是因为为了检测use-after-free问题,当free object时会在将object填充magic num(0x6b)。如果不后移的话,岂不是破坏了object之间的单链表关系。

3.1. Red zone有什么用

从图中我们可以看到在object后面紧接着就是Red zone区域,那么Red zone有什么作用呢?既然紧随其后,自然是检测右边界越界访问(right out-of-bounds access)。原理很简单,在Red zone区域填充magic num,检查Red zone区域数据是否被修改即可知道是否发生right oob。

可能你会想到如果越过Redzone,直接改写了FP,岂不是检测不到oob了,并且链表结构也被破坏了。其实在check_object()函数中会调用check_valid_pointer()来检查FP是否valid,如果invalid,同样会print error syslog。

3.2. padding有什么用

padding是sizeof(void *) bytes的填充区域,在分配slab缓存池时,会将所有的内存填充0x5a。同样在free/alloc object的时候作为检测的一种途径。如果padding区域的数据不是0x5a,就代表发生了“Object padding overwritten”问题。这也是有可能,越界跨度很大。

3.3. red_left_pad有什么用

red_left_pad和Red zone的作用一致。都是为了检测oob。区别就是Red zone检测right oob,而red_left_pad是检测left oob。如果仅仅看到上面图片中object layout。你可能会好奇,如果发生left oob,那么应该是前一个object的red_left_pad区域被改写,而不是当前object的red_left_pad。如果你注意到这个问题,还是很机智的,这都被你发现了。为了避免这种情况的发生,SLUB allocator在初始化slab缓存池的时候会做一个转换。

基于SLUB的DEBUG功能,如何帮忙检测内存越界和访问已经释放的内存

如果你去追踪kmem_cache_create(),在calculate_sizes()中布局object。区域划分的layout就如同你看到上图的上半部分。当我第一次看到这段代码的时候,我也这么认为。实际上却不是这样的。在struct page结构中有一个freelist指针,freelist会指向第一个available object。在构建object之间的单链表的时候,object首地址实际上都会加上一个red_left_pad的偏移,这样实际的layout就如同图片中转换之后的layout。为什么会这样呢?因为在有SLUB DEBUG功能的时候,并没有检测left oob功能。这种转换是后续一个补丁的修改。补丁就是为了增加left oob检测功能。

做了转换之后的red_left_pad就可以检测leftoob。检测的方法和Red zone区域一样,填充的magic num也一样,差别只是检测的区域不一样而已。

4. SLUB DEBUG原理

经过上一节分析应该很清楚了大概的原理了。从high level考虑,SLUB就是利用特殊区域填充特殊的magic num,在每一次alloc/free的时候检查magic num是否被意外修改。

4.1. magic num

SLUB 中有哪些magic num呢?所有使用的magic num都宏定义在include/linux/poison.h文件。

基于SLUB的DEBUG功能,如何帮忙检测内存越界和访问已经释放的内存

SLUB_RED_INACTIVE和SLUB_RED_ACTIVE用来填充Red zone和red_left_pad,目的是检测oob。POISON_INUSE用来填充padding区域,同样可以用来检测oob,只不过是poison overwrite。POISON_FREE作用是检测use-after-free问题。POISON_END是object可用区域的最后一个字节填充。

4.2. slab缓存池填充

当SLUB allocator申请一块内存作为slab 缓存池的时候,会将整块内存填充POISON_INUSE。如下图所示。

然后通过init_object()函数将相关的区域填充成free object的情况,并且建立单链表。注意freelist指针指向的位置,SLUB_DEBUG on和off的情况下是不一样的。主要就是3.3节提到的转换关系。为什么这里填充成freeobject的情况呢?其实就是为了假装我这里都是free的object,也是符合情理的。object初始化流程如下。

基于SLUB的DEBUG功能,如何帮忙检测内存越界和访问已经释放的内存

4.3. free objectlayout

刚分配slab缓存池和free object之后,系统都会通过调用init_object()函数初始化object各个区域,主要是填充magic num。free object layout如下图所示。

基于SLUB的DEBUG功能,如何帮忙检测内存越界和访问已经释放的内存

red_left_pad和Red zone填充了SLUB_RED_INACTIVE(0xbb);

object填充了POISON_FREE(0x6b),但是最后一个byte填充POISON_END(0xa5);

padding在allocate_slab的时候就已经被填充POISON_INUSE(0x5a),如果程序意外改变,当检测到padding被改变的时候,会output error syslog并继续填充0x5a。

4.4. alloc object layout

当从SLUB allocator申请一个object时,系统同样会调用init_object()初始化成想要的模样。alloc object layout如下图所示。

基于SLUB的DEBUG功能,如何帮忙检测内存越界和访问已经释放的内存

red_left_pad和Red zone填充了SLUB_RED_ACTIVE(0xcc);

object填充了POISON_FREE(0x6b),但是最后一个byte填充POISON_END(0xa5);

padding在allocate_slab的时候就已经被填充POISON_INUSE(0x5a),如果程序意外改变,当检测到padding被改变的时候,会output error syslog并继续填充0x5a。

alloc object layout和free object layout相比较而言,也仅仅是red_left_pad和Red zone的不同。既然该填充的数据都搞定了,下面就是如何检查oob、use-after-free等问题了。

4.5. out-of-bounds bugs detect

下面使用demo例程来说明oob检测。我们使用kmalloc分配32 bytes内存,然后制造越界访问第33个元素,必然会越界访问。由于kmalloc是基于SLUB allocator,因此此bug可以检测。

基于SLUB的DEBUG功能,如何帮忙检测内存越界和访问已经释放的内存

运行后的object layout如下图所示。

基于SLUB的DEBUG功能,如何帮忙检测内存越界和访问已经释放的内存

我们可以看到,Red zone区域本来应该0xcc的地方被修改成了0x88。很明显这是一个Redzone overwritten问题。那么系统什么时候会检测到这个严重的bug呢?就在你kfree()之后。kfree()中会去检测释放的object中各个区域的值是否valid。Redzone区域的值全是0xcc就是valid,因此这里会检测0x88不是0xcc,进而输出errorsyslog。kfree()最终会调用free_consistency_checks()检测object。free_consistency_checks()函数如下。

基于SLUB的DEBUG功能,如何帮忙检测内存越界和访问已经释放的内存

check_valid_pointer()负责检测object的free pointer指针数据是否valid。oob是有可能导致这种情况得到发生。

on_freelist()检测object是否已经free,可以检测多次free的bug。

check_object()会检测Red zone区域的数值是否被改变,因此这里就会报出bug。

如果是左边界越界访问,是否也同样可以检测出呢?可以测试以下demo例程。

基于SLUB的DEBUG功能,如何帮忙检测内存越界和访问已经释放的内存

运行后的object layout如下图所示。

基于SLUB的DEBUG功能,如何帮忙检测内存越界和访问已经释放的内存

检测方法大同小异,这里也是最终在free_consistency_checks()函数中通过检测red_left_pad区域发现left oob问题。

可能你会想如果我只申请内存不释放的话,这个bug还能检测到吗?其实这里是不行的。我们只能借助slabinfo工具主动触发检测功能。所以,这也是SLUB DEBUG的一个劣势,它不能做到动态监测。它的检测机制是被动的。

4.6. use-after-free bugs detect

如果是use-after-free问题,我们该如何检测呢?首先上demo例程。

基于SLUB的DEBUG功能,如何帮忙检测内存越界和访问已经释放的内存

运行之后object layout如下图所示。

基于SLUB的DEBUG功能,如何帮忙检测内存越界和访问已经释放的内存

还记得上面说的吗?SLUB DEBUG是被动的。因此这里就要选择slabinfo工具了。命令中断输入slabinfo –v即可。slabinfo检测的原理也很简单,便利所有已经释放的object,检查object区域是否全是0x6b(最后一个字节oxa5)即可,如果不是的话,自然就是use-after-free。

5. slabinfo

我们看一下slabinfo –v命令的实现方式以及检查的流程。slabinfo源码位于tools/vm/slabinfo.c文件。slabinfo –v命令执行流程如下图所示。

基于SLUB的DEBUG功能,如何帮忙检测内存越界和访问已经释放的内存

针对系统中每一个slab都会执行set_obj()函数。set_obj()代码如下:

基于SLUB的DEBUG功能,如何帮忙检测内存越界和访问已经释放的内存

set_obj()参数name传递的是“validate”,n传递的是1。作用就是向/sys/kernel/slab// validate节点写“1”触发slab检测功能。根据validate节点找到写入口函数validate_store()。调用函数执行流如下图所示。

基于SLUB的DEBUG功能,如何帮忙检测内存越界和访问已经释放的内存

validate_slab()代码如下:

基于SLUB的DEBUG功能,如何帮忙检测内存越界和访问已经释放的内存

check_slab()会调用slab_pad_check()检查slab padding区域。slab padding和object里面的pading不是一回事。如果说从buddy system分配的页按照SLUB规则平分成很多object,那么有可能不能整除,那么剩下的unused区域就是slab padding。valid的数值是0x5a。如下图所示。

get_map()利用bitmap标记所有的available object。例如,slab缓存池一共有10个对象,按地址大小排序标号0-9(相当于index)。假设5和8号object已经被分配出去。那么bitmap中除了bit5和bit8以为,其余位为1。

第一个for循环遍历所有的available object是否有oob、use-after-free、object padding overwritten等问题发生。

第二个for循环遍历所有已经分配出去的object是否发生oob问题。

基于SLUB的DEBUG功能,如何帮忙检测内存越界和访问已经释放的内存

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    87

    文章

    11199

    浏览量

    208691
  • 内存
    +关注

    关注

    8

    文章

    2962

    浏览量

    73803
  • DEBUG
    +关注

    关注

    3

    文章

    89

    浏览量

    19844

原文标题:宋牧春: Linux内核slab内存的越界检查——SLUB_DEBUG

文章出处:【微信号:LinuxDev,微信公众号:Linux阅码场】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    深度解析内存管理SLUB DEBUG原理

     SLUB DEBUG检测oob问题原理也很简单,既然为了发现是否越界,那么就在分配出去的内存尾部添加一段额外的
    发表于 10-02 11:30 3907次阅读
     深度解析<b class='flag-5'>内存</b>管理<b class='flag-5'>SLUB</b> <b class='flag-5'>DEBUG</b>原理

    关于内存释放

    刚看到坛子里有个朋友说两个while循环就让内存占用达到100%,而一个就没问题,我怀疑是死循环或者内存释放,大家有兴趣的也来讨论下,关于labview内存
    发表于 06-15 09:05

    linux的内存释放操作

    在Linux系统下,我们一般不需要去释放内存,因为系统已经内存管理的很好。但是凡事也有例外,有的时候内存会被缓存占用掉,导致系统使用SWA
    发表于 07-26 07:05

    C语言内存操作的陷阱!你踩过坑吗?

    1、返回局部变量的地址,或者返回指向局部变量的指针int *stackref(){ int val; return &val;}2、引用已经释放了的堆内存(野指针)int
    发表于 09-26 16:02

    【原创】常见的内存错误及对策

    ,这是很危险的,也是经常出错的地方。4、内存已经释放了,但是继续通过指针来使用一般会有以下三种情况:①就是上面所说的,free(p)之后,继续通过p指针来访问
    发表于 08-24 11:34

    如何在Win 2003中安全的释放内存

    如何在Win 2003中安全的释放内存 经常看到有朋友在论坛上诉苦说需要释放内存,其实如果你安装了Windows 2003的话,那么并不需要寻求
    发表于 01-29 11:36 675次阅读

    如何释放电脑内存

    如何释放电脑内存 电脑在用久以后,大家是不是感觉到速度越来越慢?想释放内存还必须重启计算机,是不是很麻烦?本人介绍一种
    发表于 03-01 10:23 2601次阅读

    内存泄漏的检测方法

    内存泄露(Memory leak)指的是,在程序中动态申请的内存,在使用完后既没有释放,又无法被程序的其他部分访问内存泄露是在开发大型程序
    的头像 发表于 06-20 11:01 3103次阅读

    进程虚拟内存布局以及进程的虚拟内存分配释放流程,涉及的代码

    我们计划通过一系列文章来介绍虚拟内存分配/释放,缺页处理,内存压缩/回收,内存分配器等知识,梳理虚拟内存的管理。本章节结合代码介绍进程虚拟
    的头像 发表于 06-28 09:38 4001次阅读

    一文详解Linux内存检测技术

    不同的工具有不同的侧重点,本章主要从slub_debug、kmemleak、kasan三个工具介绍。
    的头像 发表于 05-20 18:00 3069次阅读
    一文详解Linux<b class='flag-5'>内存</b><b class='flag-5'>检测</b>技术

    slub分配内存原理 slub数据结构之间关系

    slub的数据结构相对于slab来说要简单很多。并且对外接口和slab兼容。所以说,从slab的系统更换到slub,可以说是易如反掌。
    发表于 04-03 09:20 593次阅读

    valgrind检测内存问题的原理

    valgrind 是一个提供了一些 debug 和优化的工具的工具箱,可以使得你的程序减少内存泄漏或者错误访问。valgrind 默认使用 memcheck 去检查内存问题。memch
    的头像 发表于 05-23 09:30 2230次阅读
    valgrind<b class='flag-5'>检测</b><b class='flag-5'>内存</b>问题的原理

    free在释放内存的时候,为什么不需要指定内存的大小?

    malloc在申请内存的时候,需要指定内存的大小,申请成功则返回这块内存的地址,但是free的时候,只需要指定释放内存的起始地址,系统就知
    的头像 发表于 09-15 17:05 1570次阅读
    free在<b class='flag-5'>释放</b><b class='flag-5'>内存</b>的时候,为什么不需要指定<b class='flag-5'>内存</b>的大小?

    如何使用valgrind对代码进行内存泄露检测

    代码可能存在 内存泄露 怎么办? 使用 valgrind 可以对代码进行内存泄露检测。 valgrind下载安装 安装: 1 、tar –jxvf valgrind- 3 . 21 . 0
    的头像 发表于 10-04 14:56 770次阅读
    如何使用valgrind对代码进行<b class='flag-5'>内存</b>泄露<b class='flag-5'>检测</b>

    常用的解决内存错误的方法

    1. 内存管理功能问题 由于C++语言对内存有主动控制权,内存使用灵活和效率高,但代价是不小心使用就会导致以下内存错误: • memory
    的头像 发表于 11-10 15:29 1437次阅读
    常用的解决<b class='flag-5'>内存</b>错误的方法