init_object()是 SLUB 调试里用于初始化对象内存内容的函数,把对象的有效数据区填成指定字节,用于 poison 机制。
一、函数签名
static void init_object(struct kmem_cache *s, void *object, u8 val)
| 参数 | 含义 |
|---|---|
s | 缓存 |
object | 对象指针 |
val | 要填充的字节值 |
无返回值。
二、典型实现
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 (slub_debug_orig_size(s) && val == SLUB_RED_INACTIVE) { /* 对象未使用,不填充有效数据区 */ return; } if (s->flags & SLAB_POISON) memset(p, val, s->size - s->red_left_pad); }具体条件随版本有差异,但核心动作是:
填左红区:若开启
SLAB_RED_ZONE,把对象前面的左红区填成val。填对象数据区:若开启
SLAB_POISON,把对象有效数据区填成val。可能跳过 orig_size 区域:避免覆盖 orig_size 元数据。
三、val的取值:poison 字节
val由调用者传入,通常来自内核定义的 poison 常量:
| 常量 | 值 | 含义 |
|---|---|---|
POISON_FREE | 0x6b('k') | 对象空闲时填充,表示“已释放,可用” |
POISON_INUSE | 0x5a('Z') | 对象使用中填充,检测未初始化读 |
SLUB_RED_INACTIVE | 0xbb | 红区未激活(正常)填充 |
SLUB_RED_ACTIVE | 0xcc | 红区激活(已分配)填充 |
两种典型调用
分配时:对象从空闲变已分配,先填POISON_INUSE:
// alloc_debug_processing init_object(s, object, POISON_INUSE);
释放时:对象从已分配变空闲,填POISON_FREE:
// free_debug_processing init_object(s, object, POISON_FREE);
四、poison 机制的检测原理
分配时填 0x5a 0x5a 0x5a ... │ ├─ 用户正常使用,写入数据,覆盖 0x5a │ ├─ 若某处读到仍是 0x5a → 说明“使用了未初始化内存” │ └─ 释放时填 0x6b 0x6b 0x6b ... │ ├─ 若之后又读到 0x5a 或其他值 → 说明“释放后被写”(use-after-free) │ └─ 若释放时发现还是 0x5a 原样 → 说明“对象从未被使用过”(可能泄漏)
init_object()就是打上这些标记的写入端。
五、红区填充
if (s->flags & SLAB_RED_ZONE) memset(p - s->red_left_pad, val, s->red_left_pad);
p - s->red_left_pad:对象真实起点(含左红区)。填
red_left_pad字节。右红区通常由其他逻辑填充(如
set_redzone)。红区正常应保持特定值,若被改写说明越界写。
六、使用场景
init_object()在调试路径中成对出现:
// 分配:标记为“使用中” static int alloc_debug_processing(struct kmem_cache *s, struct slab *slab, void *object, unsigned long addr) { ... init_object(s, object, POISON_INUSE); set_track(s, object, TRACK_ALLOC, addr, GFP_ATOMIC); ... } // 释放:标记为“空闲” static int free_debug_processing(struct kmem_cache *s, struct slab *slab, void *object, unsigned long addr) { ... init_object(s, object, POISON_FREE); set_track(s, object, TRACK_FREE, addr, GFP_ATOMIC); ... }配合check_object()校验:
// check_object 发现 poison 值不符 → 报告 use-after-free 或未初始化使用 if (s->flags & SLAB_POISON) { if (val != POISON_FREE && val != POISON_INUSE) { object_err(s, slab, object, "Poison overwritten"); } }七、门控与零开销
未开
SLAB_POISON且未开SLAB_RED_ZONE:两个if都不成立,函数什么都不做。未编译
CONFIG_SLUB_DEBUG:整个函数可能不编译或退化为空。static:仅本文件内使用,编译器可内联或消除。生产内核里
init_object()调用点可能直接不执行,零开销。
八、与前面知识的呼应
| 前面讲过 | 这里体现 |
|---|---|
SLAB_POISON | 控制对象数据区填充 |
SLAB_RED_ZONE | 控制左红区填充 |
red_left_pad | 左红区大小 |
POISON_FREE/POISON_INUSE | val的典型取值 |
check_object() | 校验 poison 是否被破坏 |
set_orig_size() | 可能与init_object配合,避免覆盖 orig_size |
print_trailer() | 出错时打印 poison 区域内容 |
九、一句话总结
init_object(s, object, val)把对象的左红区和有效数据区填成指定 poison 字节val:分配时填POISON_INUSE(0x5a)标记“使用中”,释放时填POISON_FREE(0x6b)标记“已空闲”。它是 SLUB poison 机制的写入端,配合check_object()检测未初始化使用、use-after-free、越界写,未开SLAB_POISON/SLAB_RED_ZONE时零开销。