slub debug(linux4.16.1)

article/2025/11/9 11:35:15

在命令行选项中添加slub_debug=UFPZ,以使能相关检测功能,其含义如下:
(1)U:跟踪该slab内存的相关属性,如其创建时所属的cpu、进程、时间

(2)F:开启sanity检查功能。该功能用于在slab的某些操作中(如slab分配和释放时),检测是否有对该slab的非法内存访问操作

(3)P:开启poisoning功能,它可被用于检测再次访问释放后的内存问题。当slab被释放后,slab object本身内存将被填充为特定值。因此一旦其释放后再次访问该slab,则通过检查slab内存中的值是否被修改,即可检测到

(4)Z:开启redzoning功能,它可被用于检测内存越界访问问题。其会在slab object的前面和后面分别添加red zone,并在其被分配和被释放时,分别用不同的值填充。当发生访问越界时,就可以通过检查redzone的值检测到

如果命令行添加成功后,通过如下命令可以看到

cat /proc/cmdline
BOOT_IMAGE=/boot/vmlinuz-3.13.0-24-generic root=UUID=10426707-be58-4d00-b49b-f1fe35345d57 ro find_preseed=/preseed.cfg auto noprompt priority=critical locale=en_US quiet

开启slub debug后,obj被分配出来的时候,整个obj会被初始化为特定的数值

以下的都是假定开启了slub deubg

当内存被分配出来的时候全部的区域都被初始化为了POISON_INUSE(0x5a)

#define SLUB_RED_INACTIVE	0xbb
#define SLUB_RED_ACTIVE		0xcc/* ...and for poisoning */
#define	POISON_INUSE	0x5a	/* for use-uninitialised poisoning */
#define POISON_FREE	0x6b	/* for use-after-free poisoning */
#define	POISON_END	0xa5	/* end-byte of poisoning */
static struct page *allocate_slab(struct kmem_cache *s, gfp_t flags, int node)
{
............................if (unlikely(s->flags & SLAB_POISON))memset(start, POISON_INUSE, PAGE_SIZE << order);kasan_poison_slab(page);shuffle = shuffle_freelist(s, page);if (!shuffle) {for_each_object_idx(p, idx, s, start, page->objects) {setup_object(s, page, p);if (likely(idx < page->objects))set_freepointer(s, p, p + s->size);elseset_freepointer(s, p, NULL);}page->freelist = fixup_red_left(s, start);}..................return page;
}

setup_object-> setup_object_debug

static void setup_object_debug(struct kmem_cache *s, struct page *page,void *object)
{if (!(s->flags & (SLAB_STORE_USER|SLAB_RED_ZONE|__OBJECT_POISON)))return;init_object(s, object, SLUB_RED_INACTIVE);init_tracking(s, object);
}
static void init_object(struct kmem_cache *s, void *object, u8 val)
{u8 *p = object;if (s->flags & SLAB_RED_ZONE)memset(p - s->red_left_pad, val, s->red_left_pad);if (s->flags & __OBJECT_POISON) {memset(p, POISON_FREE, s->object_size - 1);p[s->object_size - 1] = POISON_END;}if (s->flags & SLAB_RED_ZONE)memset(p + s->object_size, val, s->inuse - s->object_size);
}

slub分配器将内存刚开始划分出来的时候长这个样子,如下图所示: 

red left pad用于检查向左写越界的问题。red zone用于检查向右检查写越界的问题。alloc/free track用于根据释放和申请的调用信息。

slub debug检测时机:

1、申请时

___slab_alloc->alloc_debug_processing

可以看到在申请的完了之后先会进行检查alloc_consistency_checks。

检查完毕了,在重新初始化整个块。同时,这个时候red zone区域的值就从SLUB_RED_INACTIVE被修改为了SLUB_RED_ACTIVE(init_object(s, object, SLUB_RED_ACTIVE);)

static noinline int alloc_debug_processing(struct kmem_cache *s,struct page *page,void *object, unsigned long addr)
{if (s->flags & SLAB_CONSISTENCY_CHECKS) {if (!alloc_consistency_checks(s, page, object, addr))goto bad;}/* Success perform special debug activities for allocs */if (s->flags & SLAB_STORE_USER)set_track(s, object, TRACK_ALLOC, addr);trace(s, page, object, 1);init_object(s, object, SLUB_RED_ACTIVE);return 1;bad:if (PageSlab(page)) {/** If this is a slab page then lets do the best we can* to avoid issues in the future. Marking all objects* as used avoids touching the remaining objects.*/slab_fix(s, "Marking all objects used");page->inuse = page->objects;page->freelist = NULL;}return 0;
}

static inline int alloc_consistency_checks(struct kmem_cache *s,struct page *page,void *object, unsigned long addr)
{if (!check_slab(s, page))return 0;if (!check_valid_pointer(s, page, object)) {object_err(s, page, object, "Freelist Pointer check fails");return 0;}/* 检测对象里面的内存布局的值是否被改变这些 */if (!check_object(s, page, object, SLUB_RED_INACTIVE))return 0;return 1;
}

 检查各个区域的值,是不是我们设定的固定值。如果不是,将错误信息打印出来,并将错误的值恢复回来。

static int check_object(struct kmem_cache *s, struct page *page,void *object, u8 val)
{u8 *p = object;u8 *endobject = object + s->object_size;if (s->flags & SLAB_RED_ZONE) {/* 检查left padding */if (!check_bytes_and_report(s, page, object, "Redzone",object - s->red_left_pad, val, s->red_left_pad))return 0;if (!check_bytes_and_report(s, page, object, "Redzone",endobject, val, s->inuse - s->object_size))return 0;} else {if ((s->flags & SLAB_POISON) && s->object_size < s->inuse) {check_bytes_and_report(s, page, p, "Alignment padding",endobject, POISON_INUSE,s->inuse - s->object_size);}}if (s->flags & SLAB_POISON) {if (val != SLUB_RED_ACTIVE && (s->flags & __OBJECT_POISON) &&(!check_bytes_and_report(s, page, p, "Poison", p,POISON_FREE, s->object_size - 1) ||!check_bytes_and_report(s, page, p, "Poison",p + s->object_size - 1, POISON_END, 1)))return 0;/** check_pad_bytes cleans up on its own.*/check_pad_bytes(s, page, p);}if (!s->offset && val == SLUB_RED_ACTIVE)/** Object and freepointer overlap. Cannot check* freepointer while object is allocated.*/return 1;/* Check free pointer validity */if (!check_valid_pointer(s, page, get_freepointer(s, p))) {object_err(s, page, p, "Freepointer corrupt");/** No choice but to zap it and thus lose the remainder* of the free objects in this slab. May cause* another error because the object count is now wrong.*/set_freepointer(s, p, NULL);return 0;}return 1;
}

2、释放时

void kfree(const void *x)
{struct page *page;void *object = (void *)x;trace_kfree(_RET_IP_, x);if (unlikely(ZERO_OR_NULL_PTR(x)))return;page = virt_to_head_page(x);if (unlikely(!PageSlab(page))) {BUG_ON(!PageCompound(page));kfree_hook(object);__free_pages(page, compound_order(page));return;}slab_free(page->slab_cache, page, object, NULL, 1, _RET_IP_);
}
static void __slab_free(struct kmem_cache *s, struct page *page,void *head, void *tail, int cnt,unsigned long addr){void *prior;int was_frozen;struct page new;unsigned long counters;struct kmem_cache_node *n = NULL;unsigned long uninitialized_var(flags);stat(s, FREE_SLOWPATH);if (kmem_cache_debug(s) &&!free_debug_processing(s, page, head, tail, cnt, addr))return;...........................
}

slub分配器在释放obj的时候,在这里进行检查。同时释放的时候会重新初始化obj的魔数字。

static noinline int free_debug_processing(struct kmem_cache *s, struct page *page,void *head, void *tail, int bulk_cnt,unsigned long addr)
{
..............................if (s->flags & SLAB_CONSISTENCY_CHECKS) {if (!free_consistency_checks(s, page, object, addr))goto out;}if (s->flags & SLAB_STORE_USER)set_track(s, object, TRACK_FREE, addr);trace(s, page, object, 0);/* Freepointer not overwritten by init_object(), SLAB_POISON moved it */init_object(s, object, SLUB_RED_INACTIVE);
................................return ret;
}
static inline int free_consistency_checks(struct kmem_cache *s,struct page *page, void *object, unsigned long addr)
{if (!check_valid_pointer(s, page, object)) {//检查obj指针是否正确slab_err(s, page, "Invalid object pointer 0x%p", object);return 0;}if (on_freelist(s, page, object)) {object_err(s, page, object, "Object already free");return 0;}if (!check_object(s, page, object, SLUB_RED_ACTIVE))return 0;if (unlikely(s != page->slab_cache)) {if (!PageSlab(page)) {slab_err(s, page, "Attempt to free object(0x%p) outside of slab",object);} else if (!page->slab_cache) {pr_err("SLUB <none>: no slab for object 0x%p.\n",object);dump_stack();} elseobject_err(s, page, object,"page slab pointer corrupt.");return 0;}return 1;
}

总结

slub deubg是利用在obj中填充魔数字,在释放和申请的时候都去检查魔数字是否被改变,进而检测到是否有越界改写对象等问题。

内存越界:可以看到obj的左右都填充了固定的数字。如果写越界了,我们检查左右的魔数字,就能被检测出来;

访问已经释放的内存:obj被释放的时候,整个obj的内存布局会被重新初始化,如果我们去访问已经释放的内存,当该obj被再次分配的时候,就能被检测出来;

重复释放:当obj被释放的时候,会去freelist上面找该对象。如果有找到,稍后会去挂到freelist上面。如果找到了,这个就出现了重复释放的问题。

1、但是可以看到slub debug只能在申请和是否的时候去检查。其他时候检查不到。因此该方法具有迟滞性,不能在出问题的时候,就检测出来。

2、另外开启slub debug会让申请的内存块变大,增加内存开销。

3、只能检查到通过slub 分配器分配的内存块。对于全局变量或者栈的问题无法被检测到。

如何主动去触发slub debug检查呢?

 slub debug在发生slab内存非法操作时,不能立即检测出来。而需要手动通过sysfs的validate属性触发检测操作。如要检测128字节kmalloc对象,则可使用以下命令:

echo 1 > /sys/kernel/slab/kmalloc-128/validate

好像是去调用这个函数,去对kmalloc-128池子里面的obj进行检查 

static int validate_slab(struct kmem_cache *s, struct page *page,unsigned long *map)
{void *p;void *addr = page_address(page);if (!check_slab(s, page) ||!on_freelist(s, page, NULL))return 0;/* Now we know that a valid freelist exists */bitmap_zero(map, page->objects);get_map(s, page, map);for_each_object(p, s, addr, page->objects) {if (test_bit(slab_index(p, s, addr), map))if (!check_object(s, page, p, SLUB_RED_INACTIVE))return 0;}for_each_object(p, s, addr, page->objects)if (!test_bit(slab_index(p, s, addr), map))if (!check_object(s, page, p, SLUB_RED_ACTIVE))return 0;return 1;
}

样例

qemu-system-arm -M vexpress-a9 -m 1024M -kernel arch/arm/boot/zImage -append "rdinit=/linuxrc console=ttyAMA0 loglevel=8 slub_debug" -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb -nographic

马上触发slab检测

 / # echo 1 > /sys/kernel/slab/kmalloc-64/validate 

 从kmalloc-64里面申请的内存,释放之后再次去访问

struct obj{int a;int b;char c;short d;int arr[8];
};
void myCallback(void)
{struct obj *p = kmalloc(sizeof(struct obj), GFP_ATOMIC);if (!p)return;kfree(p);p->c = 0x88;
}

 用的linux 3.16代码进行演示。。。(没有left red zone)

=============================================================================
BUG kmalloc-64 (Not tainted): Poison overwritten
-----------------------------------------------------------------------------

Disabling lock debugging due to kernel taint
INFO: 0xee057f08-0xee057f08. First byte 0x88 instead of 0x6b
INFO: Allocated in myCallback+0xb8/0xf8 age=1021 cpu=0 pid=0
INFO: Freed in myCallback+0xc4/0xf8 age=1021 cpu=0 pid=0
INFO: Slab 0xef5baae0 objects=32 used=32 fp=0x  (null) flags=0x0080
INFO: Object 0xee057f00 @offset=3840 fp=0xee057680

Bytes b4 ee057ef0: 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a  ZZZZZZZZZZZZZZZZ
Object ee057f00: 6b 6b 6b 6b 6b 6b 6b 6b 88 6b 6b 6b 6b 6b 6b 6b  kkkkkkkk.kkkkkkk
Object ee057f10: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b  kkkkkkkkkkkkkkkk
Object ee057f20: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b  kkkkkkkkkkkkkkkk
Object ee057f30: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b a5  kkkkkkkkkkkkkkk.
Redzone ee057f40: bb bb bb bb                                      ....
Padding ee057f68: 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a  ZZZZZZZZZZZZZZZZ
Padding ee057f78: 5a 5a 5a 5a 5a 5a 5a 5a                          ZZZZZZZZ
CPU: 0 PID: 758 Comm: linuxrc Tainted: G    B         3.16.0 #144
[<c0013f8c>] (unwind_backtrace) from [<c0010f64>] (show_stack+0x10/0x14)
[<c0010f64>] (show_stack) from [<c044ef5c>] (dump_stack+0x74/0x90)
[<c044ef5c>] (dump_stack) from [<c00c3844>] (check_bytes_and_report+0xb8/0x100)
[<c00c3844>] (check_bytes_and_report) from [<c00c3a14>] (check_object+0x188/0x21c)
[<c00c3a14>] (check_object) from [<c044e094>] (alloc_debug_processing+0x134/0x158)
[<c044e094>] (alloc_debug_processing) from [<c044e390>] (__slab_alloc.constprop.53+0x2d8/0x2f4)
[<c044e390>] (__slab_alloc.constprop.53) from [<c00c4d08>] (kmem_cache_alloc+0xb8/0xe4)
[<c00c4d08>] (kmem_cache_alloc) from [<c03968cc>] (sock_alloc_inode+0x2c/0x98)
[<c03968cc>] (sock_alloc_inode) from [<c00e055c>] (alloc_inode+0x1c/0x9c)
[<c00e055c>] (alloc_inode) from [<c00e2010>] (new_inode_pseudo+0x8/0x4c)
[<c00e2010>] (new_inode_pseudo) from [<c039707c>] (sock_alloc+0x14/0x9c)
[<c039707c>] (sock_alloc) from [<c0397efc>] (__sock_create+0x44/0x198)
[<c0397efc>] (__sock_create) from [<c03980d0>] (sock_create+0x44/0x4c)
[<c03980d0>] (sock_create) from [<c0398594>] (SyS_socket+0x2c/0xac)
[<c0398594>] (SyS_socket) from [<c000e420>] (ret_fast_syscall+0x0/0x30)
FIX kmalloc-64: Restoring 0xee057f08-0xee057f08=0x6b

FIX kmalloc-64: Marking all objects used
 

 像这种释放后再访问的问题

1、首先我们知道申请的对象是64字节的

BUG kmalloc-64 (Not tainted): Poison overwritten

 2、我们知道是该对象的第9字节被改写

Object ee057f00: 6b 6b 6b 6b 6b 6b 6b 6b 88 6b 6b 6b 6b 6b 6b 6b  kkkkkkkk.kkkkkkk

3、该对象申请地方myCallback

INFO: Allocated in myCallback+0xb8/0xf8 age=1021 cpu=0 pid=0
INFO: Freed in myCallback+0xc4/0xf8 age=1021 cpu=0 pid=0 

在我这个例子里面知道是申请的 struct obj,再根据第9字节,可以知道修改的是obj的成员c


http://chatgpt.dhexx.cn/article/9Th1D8Qi.shtml

相关文章

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…

电信光猫/烽火HG6543c1光猫超级密码获取改桥接模式( 中国电信浙江公司定制天翼网关3.0)

第一步&#xff1a;开telnet 浏览器网址栏输入&#xff1a;192.168.1.1:8080/cgi-bin/telnetenable.cgi?telnetenable1 第二步&#xff1a;使用pytty软件登入光猫 &#xff08;Putty软件下载&#xff1a;https://download.csdn.net/download/qq_34885669/12098009&#xff0…

保姆级-光猫改桥接-路由拨号-openwrt端口转发-阿里云DNS域名解析访问家中设备

准备&#xff1a; 1.公网ip&#xff08;江苏省电信&#xff0c;电话1分钟解决&#xff09; 2.域名(最好备案了) 3.路由器(我的是红米AC2100刷openwrt&#xff0c;重点路由器要有动态dns服务的功能&#xff0c;端口转发功能什么路由器都有) 往期教程 路由器固件刷写 红米AC210…

单臂路由 光猫桥接 一根网线复用

由于弱电箱太小&#xff0c; 二级路由拨号只能放客厅里。 这就导致其他房间无法上网了。 下面是单臂路由设置方法&#xff0c; 光猫桥接的端口下的设备&#xff0c;同样可以上网。 充分利用光猫端口。 前提&#xff1a; 光猫需要超级密码 二级路由固件为 openwrt ​​​​​