ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

slub check_slab

slub check_slab

check_slab()是 SLUB 调试中校验 slab 页级基本状态的函数,位于对象级检查(check_object)之前,先确认 slab 页本身的结构字段是否合理。

一、函数签名

static int check_slab(struct kmem_cache *s, struct slab *slab)
参数含义
s缓存
slab要校验的 slab 页

返回int:1 表示 slab 基本状态正常,0 表示发现异常。

二、典型实现

static int check_slab(struct kmem_cache *s, struct slab *slab) { int maxobj; if (!virt_addr_valid(slab)) { slab_err(s, slab, "Not a valid slab page"); return 0; } if (!PageSlab(slab_page(slab))) { slab_err(s, slab, "Not a slab page"); return 0; } maxobj = (PAGE_SIZE << oo_order(s->oo)) / s->size; if (slab->objects > maxobj) { slab_err(s, slab, "objects %u > max %u", slab->objects, maxobj); return 0; } if (slab->inuse > slab->objects) { slab_err(s, slab, "inuse %u > max %u", slab->inuse, slab->objects); return 0; } if (slab->objects == 0 && slab->inuse != 0) { slab_err(s, slab, "inuse %u, but no objects", slab->inuse); return 0; } return 1; }

逻辑分几层:

1. 地址有效性

if (!virt_addr_valid(slab)) { slab_err(s, slab, "Not a valid slab page"); return 0; }
  • virt_addr_valid(slab):检查slab指针是否是合法内核虚拟地址。

  • 防止后续字段访问触发缺页或访问非法地址。

  • 失败则报告"Not a valid slab page"。

2. 确实是 slab 页

if (!PageSlab(slab_page(slab))) { slab_err(s, slab, "Not a slab page"); return 0; }
  • slab_page(slab):从struct slab取回对应的struct page(较新内核里struct slab与struct page关系密切,可能有转换)。

  • PageSlab():页标志,确认该页确实是 SLUB 管理的 slab 页,而不是普通匿名页、页缓存页等。

  • 失败则报告"Not a slab page"——说明传进来的slab根本不是 slab。

3. 对象总数上限

maxobj = (PAGE_SIZE << oo_order(s->oo)) / s->size; if (slab->objects > maxobj) { slab_err(s, slab, "objects %u > max %u", slab->objects, maxobj); return 0; }
  • maxobj:一个 slab 页理论上最多能放的对象数,由页大小除以对象槽位大小算出。

  • 如果slab->objects超过这个上限,说明字段被破坏或缓存布局不一致。

  • 这是前面order_objects()的简化版(不含元数据扣除,用于上限判断足够)。

4. inuse 不超 objects

if (slab->inuse > slab->objects) { slab_err(s, slab, "inuse %u > max %u", slab->inuse, slab->objects); return 0; }
  • inuse(已分配对象数)不能超过objects(总对象数)。

  • 超过说明计数错误。

5. objects 为 0 时 inuse 必须为 0

if (slab->objects == 0 && slab->inuse != 0) { slab_err(s, slab, "inuse %u, but no objects", slab->inuse); return 0; }
  • 空 slab(objects == 0)不应有已分配对象。

  • 这是边界情况的一致性检查。

三、校验层次

check_slab()是第一层,只检查 slab 页的结构字段是否合理:

check_slab(s, slab) ← 本函数:页级基本状态 ├─ 地址有效? ├─ 是 slab 页? ├─ objects ≤ 上限? ├─ inuse ≤ objects? └─ objects=0 ⇒ inuse=0? validate_slab(s, slab, map) ├─ check_slab(s, slab) ← 先做页级检查 ├─ __fill_map(...) ← 填充已分配位图 └─ for_each_object(...) └─ check_object(...) ← 对象级详细校验
层次函数检查内容
页级check_slab地址、页类型、objects/inuse 合理范围
对象级check_object红区、poison、padding 内存内容
汇总validate_slab组合前两者,遍历所有对象

四、与slab_err()的配合

每个失败分支都调用slab_err():

slab_err(s, slab, "Not a slab page");

回顾slab_err():

  • __printf(3, 4)编译期格式检查。

  • KUnit 环境下静默计数。

  • __slab_bug()打印错误描述。

  • __slab_err(slab)打印 slab 状态(objects/inuse/freelist/flags)。

  • add_taint(TAINT_BAD_PAGE, ...)污染内核。

所以check_slab发现异常时,日志会自动附带 slab 状态,方便定位。

五、输出示例

============================================================================= BUG kmalloc-64 (debug): Not a slab page ... Not a slab page Slab 0xffffea0004a8c000 objects=0 used=1 fp=0x0000000000000000 flags=-

或:

BUG kmalloc-64 (debug): inuse 65 > max 64 Slab 0xffffea0004a8c000 objects=
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进