SLUB DEBUG原理

article/2025/11/10 0:25:38

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)等问题。
2.1. 如何打开功能
重新配置kernel选项,打开如下选项即可。
CONFIG_SLUB=y
CONFIG_SLUB_DEBUG=y
CONFIG_SLUB_DEBUG_ON=y
2.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
当系统开机之后,就可以运行slabinfo –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将会发生变化。如下图所示。
 12.png
SLUBU 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。
可能你会想到如果越过Red zone,直接改写了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缓存池的时候会做一个转换。
 13.png
如果你去追踪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就可以检测left oob。检测的方法和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文件。

 
  1. #define SLUB_RED_INACTIVE 0xbb
  2. #define SLUB_RED_ACTIVE 0xcc
  3. /* ...and for poisoning */
  4. #define POISON_INUSE 0x5a /* for use-uninitialised poisoning */
  5. #define POISON_FREE 0x6b /* for use-after-free poisoning */
  6. #define POISON_END 0xa5 /* end-byte of poisoning */

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。如下图所示。
 14.png
然后通过init_object()函数将相关的区域填充成free object的情况,并且建立单链表。注意freelist指针指向的位置,SLUB_DEBUG on和off的情况下是不一样的。主要就是3.3节提到的转换关系。为什么这里填充成free object的情况呢?其实就是为了假装我这里都是free的object,也是符合情理的。object初始化流程如下。
 15.png
4.3. free object layout
刚分配slab缓存池和free object之后,系统都会通过调用init_object()函数初始化object各个区域,主要是填充magic num。free object layout如下图所示。
 16.png
1) red_left_pad和Red zone填充了SLUB_RED_INACTIVE(0xbb);
2) object填充了POISON_FREE(0x6b),但是最后一个byte填充POISON_END(0xa5);
3) padding在allocate_slab的时候就已经被填充POISON_INUSE(0x5a),如果程序意外改变,当检测到padding被改变的时候,会output error syslog并继续填充0x5a。
4.4. alloc object layout
当从SLUB allocator申请一个object时,系统同样会调用init_object()初始化成想要的模样。alloc object layout如下图所示。
 17.png
1) red_left_pad和Red zone填充了SLUB_RED_ACTIVE(0xcc);
2) object填充了POISON_FREE(0x6b),但是最后一个byte填充POISON_END(0xa5);
3) 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可以检测。

 
  1. void right_oob(void)
  2. {
  3. char *p = kmalloc(32, GFP_KERNEL);
  4. if (!p)
  5. return;
  6. p[32] = 0x88;
  7. kfree(p);
  8. }

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

 
  1. static inline int free_consistency_checks(struct kmem_cache *s,
  2. struct page *page, void *object, unsigned long addr)
  3. {
  4. if (!check_valid_pointer(s, page, object)) {
  5. slab_err(s, page, "Invalid object pointer 0x%p", object);
  6. return 0;
  7. }
  8. if (on_freelist(s, page, object)) {
  9. object_err(s, page, object, "Object already free");
  10. return 0;
  11. }
  12. if (!check_object(s, page, object, SLUB_RED_ACTIVE))
  13. return 0;
  14. return 1;
  15. }

1) check_valid_pointer()负责检测object的free pointer指针数据是否valid。oob是有可能导致这种情况得到发生。
2) on_freelist()检测object是否已经free,可以检测多次free的bug。
3) check_object()会检测Red zone区域的数值是否被改变,因此这里就会报出bug。
如果是左边界越界访问,是否也同样可以检测出呢?可以测试以下demo例程。

 
  1. void left_oob(void)
  2. {
  3. char *p = kmalloc(32, GFP_KERNEL);
  4. if (!p)
  5. return;
  6. p[-1] = 0x88;
  7. kfree(p);
  8. }

运行后的object layout如下图所示。
 19.png
检测方法大同小异,这里也是最终在free_consistency_checks()函数中通过检测red_left_pad区域发现left oob问题。
可能你会想如果我只申请内存不释放的话,这个bug还能检测到吗?其实这里是不行的。我们只能借助slabinfo工具主动触发检测功能。所以,这也是SLUB DEBUG的一个劣势,它不能做到动态监测。它的检测机制是被动的。
4.6. use-after-free bugs detect
如果是use-after-free问题,我们该如何检测呢?首先上demo例程。

 
  1. void use_after_free(void)
  2. {
  3. char *p = kmalloc(32, GFP_KERNEL);
  4. if (!p)
  5. return;
  6.  
  7. kfree(p);
  8. memset(p, 0x88, 32);
  9. }

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

5. slabinfo 

我们看一下slabinfo –v命令的实现方式以及检查的流程。slabinfo源码位于tools/vm/slabinfo.c文件。slabinfo –v命令执行流程如下图所示。
 21.png
针对系统中每一个slab都会执行set_obj()函数。set_obj()代码如下:

 
  1. static void set_obj(struct slabinfo *s, const char *name, int n)
  2. {
  3. char x[100];
  4. FILE *f;
  5. snprintf(x, 100, "%s/%s", s->name, name);
  6. f = fopen(x, "w");
  7. if (!f)
  8. fatal("Cannot write to %s\n", x);
  9. fprintf(f, "%d\n", n);
  10. fclose(f);
  11. }

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

 
  1. static int validate_slab(struct kmem_cache *s, struct page *page, unsigned long *map)
  2. {
  3. void *p;
  4. void *addr = page_address(page);
  5. if (!check_slab(s, page) ||
  6. !on_freelist(s, page, NULL))
  7. return 0;
  8. /* Now we know that a valid freelist exists */
  9. bitmap_zero(map, page->objects);
  10. get_map(s, page, map);
  11. for_each_object(p, s, addr, page->objects) {
  12. if (test_bit(slab_index(p, s, addr), map))
  13. if (!check_object(s, page, p, SLUB_RED_INACTIVE))
  14. return 0;
  15. }
  16. for_each_object(p, s, addr, page->objects)
  17. if (!test_bit(slab_index(p, s, addr), map))
  18. if (!check_object(s, page, p, SLUB_RED_ACTIVE))
  19. return 0;
  20. return 1;
  21. }

1) check_slab()会调用slab_pad_check()检查slab padding区域。slab padding和object里面的pading不是一回事。如果说从buddy system分配的页按照SLUB规则平分成很多object,那么有可能不能整除,那么剩下的unused区域就是slab padding。valid的数值是0x5a。如下图所示。
2) get_map()利用bitmap标记所有的available object。例如,slab缓存池一共有10个对象,按地址大小排序标号0-9(相当于index)。假设5和8号object已经被分配出去。那么bitmap中除了bit5和bit8以为,其余位为1。
3) 第一个for循环遍历所有的available object是否有oob、use-after-free、object padding overwritten等问题发生。
4) 第二个for循环遍历所有已经分配出去的object是否发生oob问题。

 23.png

 

原创文章,转发请注明出处。蜗窝科技,www.wowotech.net。


http://chatgpt.dhexx.cn/article/pKVh8NEf.shtml

相关文章

slub allocator工作原理

前言 在Linux中&#xff0c;伙伴系统&#xff08;buddy system&#xff09;是以页&#xff08;1page等于4K&#xff09;为单位管理和分配内存。对于小内存的分配&#xff0c;如果还是使用伙伴系统进行内存分配&#xff0c;就会导致严重浪费内存。此时&#xff0c;slab分配器就应…

SLUB缓存管理

注&#xff1a;本文分析基于linux-4.18.0-193.14.2.el8_2内核版本&#xff0c;即CentOS 8.2 1、关于SLUB 目前大多数系统都不使用slab作为缓存的管理模式&#xff0c;转而使用slub&#xff0c;比如CentOS 7和8默认都是使用slub管理器。slub是基于slab的进一步优化改进&#x…

slub debug(linux4.16.1)

在命令行选项中添加slub_debugUFPZ&#xff0c;以使能相关检测功能&#xff0c;其含义如下&#xff1a; &#xff08;1&#xff09;U&#xff1a;跟踪该slab内存的相关属性&#xff0c;如其创建时所属的cpu、进程、时间 &#xff08;2&#xff09;F&#xff1a;开启sanity检查功…

linux内核内存管理slub

一、概述 linux内存管理核心是伙伴系统&#xff0c;slab&#xff0c;slub&#xff0c;slob是基于伙伴系统之上提供api&#xff0c;用于内核内存分配释放管理&#xff0c;适用于小内存&#xff08;小于&#xff11;页&#xff09;分配与释放&#xff0c;当然大于&#xff11;页…

内存管理 slub算法

内核管理页面使用了2个算法&#xff1a;伙伴算法和slub算法&#xff0c;伙伴算法以页为单位管理内存&#xff0c;但在大多数情况下&#xff0c;程序需要的并不是一整页&#xff0c;而是几个、几十个字节的小内存。于是需要另外一套系统来完成对小内存的管理&#xff0c;这就是s…

linux slub分配器,slub分配器

原标题&#xff1a;slub分配器 概述&#xff1a; Linux的物理内存管理采用了以页为单位的buddy system(伙伴系统)&#xff0c;但是很多情况下&#xff0c;内核仅仅需要一个较小的对象空间&#xff0c;而且这些小块的空间对于不同对象又是变化的、不可预测的&#xff0c;所以需要…

Linux内存管理(八): slub分配器和kmalloc

kernel: 5.10 Arch: aarch64 上文 介绍过&#xff0c; 伙伴系统在分配内存时是以物理页为单位的&#xff0c;但在很多场景下&#xff0c;内存需要分配的大小是以字节为单位的&#xff0c;达不到一个物理页的大小。如果继续使用伙伴系统进行内存分配&#xff0c; 那么就会出现严…

Linux 踩内存 slub,Linux SLUB 内存分配器分析

本文简介 本文主要介绍了Linux SLUB分配的产生原因、设计思路及其代码分析。适合于对Linux内核&#xff0c;特别是对Linux内存分配器感兴趣的读者。 1.为何需要SLUB&#xff1f; Linux SLUB内存分配器合入Linux主分支已经整整10年了&#xff01;并且是Linux目前默认的内存分配器…

SLUB

 内核管理页面使用了2个算法:伙伴算法和slub算法,伙伴算法以页为单位管理内存,但在大多数情况下,程序需要的并不是一整页,而是几个、几十个字节的小内存。于是需要另外一套系统来完成对小内存的管理,这就是slub系统。slub系统运行在伙伴系统之上,为内核提供小内存管…

linux内存源码分析 - SLUB分配器概述

本文为原创&#xff0c;转载请注明&#xff1a;http://www.cnblogs.com/tolimit/ SLUB和SLAB的区别 首先为什么要说slub分配器&#xff0c;内核里小内存分配一共有三种&#xff0c;SLAB/SLUB/SLOB&#xff0c;slub分配器是slab分配器的进化版&#xff0c;而slob是一种精简的小内…

Linux 内存管理(三)—— SLUB

目录 一、概述 二、SLUB 2.1 数据结构 2.2 初始化 2.2.1 静态建立过程 2.3 API 2.3.1 kmem_cache_create 2.3.2 kmem_cache_alloc 2.3.3 kmem_cache_free 2.3.4 kmalloc/kfree 三、参考 一、概述 伙伴系统最小的分配单位是页&#xff0c;这对于较小的内存需求会造…

SLUB内存管理的4个主要接口函数介绍(4)

slub内存管理的4个主要接口函数如下&#xff08;参考kernel-4.19&#xff09;&#xff1a; //slab缓存的创建 struct kmem_cache *kmem_cache_create(const char *name, size_t size, size_t align, unsigned long flags, void (*ctor)(void *)); //slab object的分配 void *k…

Linux内核:内存管理——SLUB分配器

SLUB和SLAB的区别 首先为什么要说slub分配器&#xff0c;内核里小内存分配一共有三种&#xff0c;SLAB/SLUB/SLOB&#xff0c;slub分配器是slab分配器的进化版&#xff0c;而slob是一种精简的小内存分配算法&#xff0c;主要用于嵌入式系统。慢慢的slab分配器或许会被slub取代&…

SLUB的引入及举例说明

我们都知道Buddy分配器是按照页的单位分配的&#xff08;Buddy系统分配器实现&#xff09;&#xff0c;如果我们需要分配几十个字节&#xff0c;几百个字节的时候&#xff0c;就需要用到SLAB分配器。 SLAB分配器专门是针对小内存分配而设计的&#xff0c;比如我们驱动中常见的…

电信光猫改桥接模式

如果只是改桥接 可以试试下面这两个地址&#xff1a;http://192.168.1.1/bridge_route.gchhttp://192.168.1.1:8080/bridge_route.gch 转载于:https://www.cnblogs.com/Devopser/p/11257535.html

电信光猫改桥接还在苦苦破解超级密码吗?

电信光猫路由改桥接&#xff0c;不同的地区有不通的方法。比较幸运的地区和终端&#xff0c;有通用的超级密码。但是不幸的地区&#xff0c;就需要通过破解这个超级密码。我就属于比较不幸的地区&#xff0c;遇到不幸的终端&#xff1a;天翼网关TEWA-708G。然后按照网上大神的破…

获取电信光猫TEWA-600超级管理密码,修改电信光猫为桥接模式

文章转载&#xff1a;玩转盒子 前些年各地运营商响应国家政策&#xff0c;光进铜退小区宽带由网线入户改成光缆入户&#xff0c;这样必须用光猫把光信号转换成电信号。我用的是中国电信的宽带&#xff0c;2017年我们小区也进行了网络改造&#xff0c;网线入户变成了光缆入户。…

友华PT921G光猫破解获取超级密码和更改桥接模式

获取超级密码 1.登陆光猫管理地址192.168.1.1 2.打开新的窗口输入&#xff1a;http://192.168.1.1/romfile.cfg ,就能下载到配置文件 3.用记事本打开romfile.cfg&#xff0c;点击编辑–>查找–>输入telecomadmin->点击查找下一个 4.查找到username“telecomadmin”,而…

为了改桥接,我决定破解中兴F450G V2光猫

还记得我之前买了个猫棒来替换光猫么&#xff1f;用了一个来月&#xff0c;发现这玩意真的不稳定&#xff0c;短则几分钟长则一两天它必定自己重启一次&#xff0c;导致我的网络时不时就会断线。这玩意不好使&#xff0c;我也没有别的光猫&#xff0c;只好找电信装维师傅给我改…

大话设计模式-桥接模式

使用场景&#xff1a;桥接模式的核心意图就是将这些实现独立出来&#xff0c;让它们各自地变化。这就使得每种实现的变化不会影响其他实现&#xff0c;从而达到应对变化的目的。 多用聚合&#xff0c;少用继承 1. 手机软件抽象类、通讯录类、游戏类 package com.hj.designPat…