☰
slub setup_slab_debug
2026/10/31 2:15:08 网站建设 项目流程

setup_slab_debug()是 SLUB 调试中初始化整个 slab 页内存为 poison 值的函数,在 slab 页刚分配、对象还没被初始化之前调用。

一、函数签名

static void setup_slab_debug(struct kmem_cache *s, struct slab *slab, void *addr)
参数含义
s缓存
slab要初始化的 slab 页
addrslab 页起始地址

无返回值。

二、典型实现

static void setup_slab_debug(struct kmem_cache *s, struct slab *slab, void *addr) { if (!kmem_cache_debug_flags(s, SLAB_POISON)) return; metadata_access_enable(); memset(kasan_reset_tag(addr), POISON_INUSE, slab_size(slab)); metadata_access_disable(); }

逻辑:

  1. 门控:未开SLAB_POISON直接返回。

  2. 临时关闭 KASAN/KMSAN:metadata_access_enable()。

  3. 填满整页:把从addr开始的slab_size(slab)字节填成POISON_INUSE。

  4. 恢复检测:metadata_access_disable()。

三、逐部分解析

门控条件

if (!kmem_cache_debug_flags(s, SLAB_POISON)) return;
  • 只有开启SLAB_POISON的缓存才做整页 poison。

  • 未开时零开销。

metadata_access_enable/disable

metadata_access_enable(); memset(...); metadata_access_disable();
  • memset的地址是 slab 页原始内存,在 KASAN/KMSAN 看来可能不属于任何合法对象(还没建立对象边界)。

  • 直接写入会触发 KASAN/KMSAN 误报。

  • 用metadata_access_enable()临时关闭检测,写入后再恢复。

  • 这是访问未建立对象边界的原始内存的标准做法。

kasan_reset_tag(addr)

memset(kasan_reset_tag(addr), POISON_INUSE, slab_size(slab));
  • 去掉 KASAN 指针 tag,得到真实物理地址。

  • 避免给带 tag 的指针做memset导致写到错误地址。

slab_size(slab)

slab_size(slab)
  • 返回该 slab 页的总字节数,即PAGE_SIZE << oo_order(s->oo)。

  • 与前面oo_order(s->oo)算页大小的思路一致,slab_size()是个封装。

POISON_INUSE

memset(..., POISON_INUSE, ...);
  • POISON_INUSE = 0x5a。

  • 表示“使用中”——但这里是初始状态,整页先填0x5a。

  • 之后每个对象会被setup_object_debug()→init_object()改成SLUB_RED_INACTIVE,再在分配/释放时改成POISON_INUSE/POISON_FREE。

四、与setup_object_debug()的关系

两者在 slab 页初始化时先后配合:

allocate_slab() │ ├─ setup_slab_debug(s, slab, addr) ← 整页先填 0x5a │ └─ memset(addr, POISON_INUSE, slab_size) │ └─ for_each_object(p, s, addr, objects) └─ setup_object_debug(s, p) ← 每个对象再精细初始化 ├─ init_object(s, p, SLUB_RED_INACTIVE) // 红区/数据区改 0xbb └─ init_tracking(s, p) // track 清零
层次函数填充
整页setup_slab_debugPOISON_INUSE(0x5a)全页
单对象setup_object_debug→init_objectSLUB_RED_INACTIVE(0xbb)红区/数据区

为什么先整页再逐对象:

  • setup_slab_debug快速把整页标记为“已使用 poison 填充”,覆盖伙伴系统残留数据。

  • setup_object_debug再按对象布局精细设置红区、track。

  • 两层保证 slab 页内所有字节都处于已知状态,任何“读到非预期值”都能被检测。

五、与check_bytes_and_report()的呼应

setup_slab_debug写入的值,正是后续校验时check_bytes_and_report()检查的目标:

  • 整页初始:POISON_INUSE。

  • 对象红区/数据区:SLUB_RED_INACTIVE或后续POISON_INUSE/POISON_FREE。

  • 校验时:发现不等于预期值 → 报告 +restore_bytes()恢复。

写入端(setup_slab_debug/setup_object_debug)与校验端(check_object/slab_pad_check)形成闭环。

六、与init_object()的区别

函数范围值时机
setup_slab_debug整页POISON_INUSEslab 页刚分配
init_object单对象红区/数据区SLUB_RED_INACTIVE/POISON_INUSE/POISON_FREE对象初始化、分配、释放
setup_object_debug单对象(组合)调init_object+init_trackingslab 页初始化时遍历对象

七、使用场景

setup_slab_debug()在allocate_slab()/new_slab()里被调用:

static struct slab *allocate_slab(...) { slab = ...; // 从伙伴系统拿页 // 调试初始化 setup_slab_debug(s, slab, slab_address(slab)); // 逐对象初始化 for_each_object(p, s, slab_address(slab), slab->objects) setup_object_debug(s, p); // 建立 freelist ... }

保证新 slab 页在投入使用前,所有内存都处于已知 poison 状态。

八、与前面知识的呼应

前面讲过这里体现
SLAB_POISON门控条件
POISON_INUSE填充值
metadata_access_enable/disable访问原始页内存时关闭 KASAN/KMSAN
kasan_reset_tag()去 tag
slab_size()/oo_order()计算页大小
setup_object_debug()逐对象精细初始化
init_object()对象级填充
check_bytes_and_report()对应的校验端

九、总结

setup_slab_debug(s, slab, addr)在开启SLAB_POISON时,用memset把整个 slab 页填成POISON_INUSE(0x5a),期间临时关闭 KASAN/KMSAN。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询