# SLUB调试相关问题汇总 调试前提:kernel 开启 debug info ,vmlinux存在符号表。 ## 如何定位 `slab_caches` 链表及相关关键结构体 ### 定位 `kmem_cache` gdb中查找到的 `slab_caches` 中存储的地址并非 `kmem_cache`结构体首地址,而是该结构体内变量 `list` 地址。 ```shell pwndbg> x/2gx &slab_caches 0xffffffff8246b140 : 0xffff88800e1cee68 0xffff88800f041068 ``` 若想获取`kmem_cache`结构体首地址需要减去`list`变量在结构体中的偏移,可见若 `name` 参数正常显示则说明获取的结构体地址正确: ```shell pwndbg> p *((struct kmem_cache*)(0xffff88800f041068-0x68)) $2 = { cpu_slab = 0x2c2c0, flags = 1073750016, min_partial = 5, size = 256, object_size = 208, reciprocal_size = { m = 1, sh1 = 1 '\001', sh2 = 7 '\a' }, offset = 104, cpu_partial = 13, oo = { x = 16 }, max = { x = 16 }, min = { x = 16 }, allocflags = 0, refcount = -1, ctor = 0x0 , inuse = 208, align = 64, red_left_pad = 0, name = 0xffffffff82208874 "kmem_cache", list = { next = 0xffffffff8246b140 , prev = 0xffff88800f041f68 }, kobj = { name = 0xffffffff82208874 "kmem_cache", entry = { next = 0xffff88800e130180, prev = 0xffff88800f041f80 }, parent = 0xffff88800f322d38, kset = 0xffff88800f322d20, ktype = 0xffffffff82471a80 , sd = 0xffff88800e19df80, kref = { refcount = { refs = { counter = 1 } } }, state_initialized = 1, state_in_sysfs = 1, state_add_uevent_sent = 0, state_remove_uevent_sent = 0, uevent_suppress = 0 }, remote_node_defrag_ratio = 1000, useroffset = 0, usersize = 0, node = {0xffff88800f040040, ...} } ``` ### 定位`kmem_cache_cpu` 需先获取 `__per_cpu_offset` 地址: ```shell pwndbg> x/4gx __per_cpu_offset 0xffffffff822c76a0 <__per_cpu_offset>: 0xffff88800f600000 0xffffffff829d3000 0xffffffff822c76b0 <__per_cpu_offset+16>: 0xffffffff829d3000 0xffffffff829d3000 ``` 用 `__per_cpu_offset`地址加上`kmem_cache`中的`cpu_slab`即可得到`kmem_cache_cpu`地址: ```shell pwndbg> p *((struct kmem_cache_cpu*)(0xffff88800f600000+0x2c2c0)) $3 = { freelist = 0xffff88800e1cef00, tid = 128, page = 0xffffea0000387380, partial = 0x0 } ``` ### 定位`kmem_cache_node` `kmem_cache`中的 `node` 变量 ```shell pwndbg> p *((struct kmem_cache_node*)0xffff88800f040040) $4 = { list_lock = { { rlock = { raw_lock = { { val = { counter = 0 }, { locked = 0 '\000', pending = 0 '\000' }, { locked_pending = 0, tail = 0 } } } } } }, nr_partial = 0, partial = { next = 0xffff88800f040050, prev = 0xffff88800f040050 }, nr_slabs = { counter = 8 }, total_objects = { counter = 128 }, full = { next = 0xffff88800f040070, prev = 0xffff88800f040070 } } ``` ### 定位`free_list` `kmem_cache_cpu`中的`freelist`变量,`freelist`链表的下一项地址为 `&freelist + offset` 变量`offset`在`kmem_cache`中。 ## 如何获取 `kmalloc-2k` 大小的slab 中的object数目 首先获得kmalloc-2k `kmem_cache`结构体中 `oo`变量的值: ```shell pwndbg> p ((struct kmem_cache*)0xffff88800f041400)->oo $7 = { x = 131080 } ``` 此处需结合源码,该slab中的object数目通过`oo_objects`函数实现: ```c //v5.1/source/mm/slub.c#L5896 void get_slabinfo(struct kmem_cache *s, struct slabinfo *sinfo) { ... sinfo->objects_per_slab = oo_objects(s->oo); ... } //v5.1/source/mm/slub.c#L343 #define OO_SHIFT 16 #define OO_MASK ((1 << OO_SHIFT) - 1) static inline unsigned int oo_objects(struct kmem_cache_order_objects x) { return x.x & OO_MASK; } ``` 计算可得:`131080 & 0xffff = 8`,即 kmalloc-2k slab中共有8个object。 该参数也可以从 `/proc/slabinfo` 中的 `objperslab`获得 ## 获取object的使用轨迹 在开启了`CONFIG_SLUB_DEBUG`配置的情况下,如果设置了`SLAB_STORE_USER`标识,将会在对象末尾加上两个track的空间大小,用于记录该对象的使用轨迹信息(分别是申请和释放的信息)。 ```c //v5.1/source/mm/slub.c#L203 /* * Tracking user of a slab. */ #define TRACK_ADDRS_COUNT 16 struct track { unsigned long addr; /* Called from address */ #ifdef CONFIG_STACKTRACE unsigned long addrs[TRACK_ADDRS_COUNT]; /* Called from address */ #endif int cpu; /* Was running on cpu */ int pid; /* Pid context */ unsigned long when; /* When did the operation occur */ }; ``` ## ubuntu18.04下使用salt调试报错:`Could not fetch register "fs_base"` 原因:qemu x64下有bug https://bugs.launchpad.net/qemu/+bug/1774149 源码编译一个qemu即可: https://wiki.qemu.org/Hosts/Linux