ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

slub restore_bytes

slub restore_bytes

restore_bytes()是 SLUB 调试里修复被破坏内存区域的辅助函数:把一段被越界写或 poison 破坏的字节恢复成期望值,并记录一条FIX日志。

一、函数签名

static void restore_bytes(struct kmem_cache *s, const char *message, u8 data, void *from, void *to)
参数含义
s缓存
message描述这段区域是什么,如"redzone"、"Poison"
data要恢复成的字节值(期望值)
from起始地址(含)
to结束地址(不含,即[from, to))

无返回值。

二、实现解析

static void restore_bytes(struct kmem_cache *s, const char *message, u8 data, void *from, void *to) { slab_fix(s, "Restoring %s 0x%p-0x%p=0x%x", message, from, to - 1, data); memset(from, data, to - from); }

两步:

1. 记录修复日志

slab_fix(s, "Restoring %s 0x%p-0x%p=0x%x", message, from, to - 1, data);
  • 用slab_fix()输出一条FIX级别日志(前面讲过,表示“发现异常并已修复”,而非致命 BUG)。

  • 打印内容:

    • message:区域描述。

    • from/to - 1:闭区间[from, to-1],比半开区间直观,方便人看。

    • data:恢复的目标值。

  • 输出形如:

FIX kmalloc-64: Restoring redzone 0xffff888012345780-0xffff88801234578f=0xbb

2. 恢复内存

memset(from, data, to - from);
  • 把[from, to)这段内存全部填成data。

  • to - from是字节数。

  • 覆盖被破坏的越界写痕迹,让对象回到合法状态。

三、修复的语义

restore_bytes()用于非致命、可恢复的破坏:

  • 红区被轻微越界写:越界写覆盖了红区的一小段,但对象本身没坏。恢复红区,让后续校验继续通过。

  • poison 值被意外改写:比如释放后某处写了脏数据,恢复成POISON_FREE。

  • padding 被破坏:填充区被覆盖,恢复成标准填充值。

与object_err()的区别:

函数语义后续动作
object_err()致命错误,无法恢复污染内核,停止使用
restore_bytes()可恢复的破坏修复并继续运行

slab_fix()的FIX前缀正好表达“发现问题 + 已修复”,而不是“BUG”。

四、与check_object()的配合

restore_bytes()的典型调用点是check_object()检测到红区或 poison 被破坏时:

static int check_bytes_and_report(struct kmem_cache *s, struct slab *slab, u8 *object, const char *what, u8 *start, unsigned int value, unsigned int bytes) { u8 *fault; u8 *end; fault = memchr_inv(start, value, bytes); // 找第一个不等于 value 的字节 if (!fault) return 1; // 全部正常 end = start + bytes; while (end > fault && end[-1] == value) end--; // 收缩到被破坏的区间 slab_bug(s, "%s overwritten", what); object_err(s, slab, object, "Object already free"); // 或类似 print_section(...); restore_bytes(s, what, value, fault, end); // 恢复被破坏区间 return 0; }

流程:

  1. 检测:用memchr_inv()找出第一个不等于期望值的字节。

  2. 报告:slab_bug/object_err/print_section输出现场。

  3. 收缩区间:把尾部也是期望值的部分排除,只保留真正被破坏的[fault, end)。

  4. 修复:restore_bytes(s, what, value, fault, end)恢复。

具体用途

  • check_bytes_and_report(..., "Left Redzone", ..., SLUB_RED_INACTIVE, ...)

  • check_bytes_and_report(..., "Right Redzone", ..., SLUB_RED_INACTIVE, ...)

  • check_bytes_and_report(..., "Padding", ..., POISON_FREE, ...)

  • check_bytes_and_report(..., "Object", ..., POISON_FREE/INUSE, ...)

message参数就是what,data是value,from/to是fault/end。

五、输出示例

一次红区被破坏后的输出:

BUG kmalloc-64 (debug): Left Redzone overwritten ... Object 0xffff888012345678 ... FIX kmalloc-64: Restoring Left Redzone 0xffff888012345770-0xffff888012345777=0xbb
  • BUG:报告检测到破坏。

  • FIX:说明已恢复破坏的字节。

六、与前面知识的呼应

前面讲过这里体现
slab_fix()输出FIX日志
slab_bug()/object_err()同一流程里先报 BUG
check_object()/check_bytes_and_report()调用restore_bytes的上层
SLUB_RED_INACTIVE/POISON_FREE等data的典型取值
print_section()报错时打印现场,修复前
memset()底层恢复操作

七、设计要点总结

  1. 检测 + 修复:SLUB 调试不仅报错,还尽量恢复,让系统继续运行以收集更多信息。

  2. 日志明确:FIX前缀区分“已修复”和“致命 BUG”。

  3. 区间语义:[from, to),日志里显示to - 1方便人读。

  4. 精确恢复:只恢复被破坏的区间,不动正常部分。

  5. 配合校验:由check_bytes_and_report()检测后调用,形成“检测→报告→收缩→恢复”闭环。

八、一句话总结

restore_bytes(s, message, data, from, to)把[from, to)这段被破坏的内存恢复成期望字节data,并用slab_fix()输出一条FIX日志说明修复了什么。它是 SLUB 调试里“可恢复破坏”的修复端,由check_bytes_and_report()在检测到红区/poison/padding 被改写后调用,与object_err()(致命错误)形成“能修则修、不能修则报 BUG”的分工。

RELATED READING

延伸阅读

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