土法炼钢兴趣小组的算法知识备份

【操作系统百科】内核内存调试

文章导航

分类入口
os
标签入口
#kasan#kfence#kmemleak#slub-debug#ubsan

目录

内核内存错误的难处在于:用户态可以靠 Valgrind 跑一遍,但内核既不能容忍 100x 的开销,也不能容忍随机崩溃在生产线上。Linux 6.x 给出了一组互补工具——KASAN 在开发期全覆盖 UAF/OOB,KFENCE 用 guard page 采样把开销压到生产可接受范围,kmemleak 抓沉默泄漏,SLUB_DEBUG 抓对象损坏,UBSAN/KCSAN 补 UB 与 data race。本文按照”开销 / 覆盖率 / 部署位置”三个维度把它们摆清楚。

一、先看图

flowchart TD
    BUG[内核内存 bug] --> TYPE{类型}
    TYPE -->|UAF/OOB| KASAN[KASAN<br/>shadow memory]
    TYPE -->|UAF/OOB 生产| KFENCE[KFENCE<br/>guard page 采样]
    TYPE -->|leak| KMEMLEAK[kmemleak<br/>GC 扫描]
    TYPE -->|corruption| SLUB[SLUB_DEBUG<br/>redzone/poison]
    KASAN --> REPORT[KASAN report<br/>stack trace]
    KFENCE --> REPORT2[KFENCE report]
    KMEMLEAK --> REPORT3[泄漏列表<br/>alloc stack]
    SLUB --> REPORT4[BUG: SLUB<br/>对象损坏]
    classDef detect fill:#388bfd22,stroke:#388bfd,color:#adbac7;
    classDef report fill:#f0883e22,stroke:#f0883e,color:#adbac7;
    class KASAN,KFENCE,KMEMLEAK,SLUB detect
    class REPORT,REPORT2,REPORT3,REPORT4 report

二、KASAN

2.1 Generic KASAN

基于 shadow memory:每 8 字节实际内存对应 1 字节 shadow(1/8 开销)。编译器在每次内存访问前插入 shadow 检查。

启用:CONFIG_KASAN=y CONFIG_KASAN_GENERIC=y

性能开销 ~2-3x CPU + ~1/8 内存。

2.2 SW Tag KASAN(arm64)

利用 TBI(Top Byte Ignore)在指针高 8 位存 tag,内存也标 tag。访问时比较——不匹配 = bug。

开销更低(~1.5-2x)但不如 generic 精确。

2.3 HW Tag KASAN(arm64 MTE)

利用 MTE 硬件做 tag 比较——几乎零 CPU 开销(~3-5%)。

CONFIG_KASAN_HW_TAGS=y,Pixel 8+ 已 dogfood。

2.4 KASAN report

BUG: KASAN: slab-use-after-free in func+0x42/0x80
Read of size 4 at addr ffff888012345678 by task myapp/1234

Allocated by task 1234:
 kmem_cache_alloc+0x...
 my_alloc_func+0x...

Freed by task 1234:
 kfree+0x...
 my_free_func+0x...

包含分配和释放的调用栈——直接定位 bug。

三、KFENCE(Kernel Electric Fence)

3.1 思路

KASAN 开销太大不能跑生产。KFENCE 用统计采样:每隔一段时间(默认 sample_interval=100 ms),把下一次 slab 分配重定向到一个独立的 guard page 池。被采样到的对象前后各有一个不可访问页,对象释放后整页继续保持不可访问。

3.2 特点

启用:CONFIG_KFENCE=y,主流发行版的 6.x 内核默认已开。

cat /sys/kernel/debug/kfence/stats
# enabled:           1
# currently allocated:  ...
# total allocations:    ...
# sample_interval (ms): 100

四、kmemleak

4.1 思路

定期(默认 10 分钟)扫描所有内核内存,查找”无引用”的分配——类似保守式 GC。所谓”无引用”指:在所有被扫描的内核内存区域里都找不到指向该对象的指针。

4.2 启用

CONFIG_DEBUG_KMEMLEAK=y
echo scan > /sys/kernel/debug/kmemleak       # 手动扫描
cat /sys/kernel/debug/kmemleak                # 查看泄漏
echo clear > /sys/kernel/debug/kmemleak       # 清除记录

4.3 报告

unreferenced object 0xffff8880... (size 128):
  comm "myapp", pid 1234, jiffies 4294967296
  backtrace:
    kmalloc+0x...
    my_driver_probe+0x...

4.4 局限

五、SLUB_DEBUG

SLUB_DEBUG 的红区、毒值、freelist 校验在 Slab/SLUB 分配器 已经介绍。这里只把它和其他工具放在一张表里比较:

工具 检测目标 适用阶段 典型开销
KASAN generic UAF / OOB / 全覆盖 开发、CI 2–3× CPU;~1/8 内存
KASAN HW tag (MTE) UAF / OOB 生产(arm64 MTE) ~3–5% CPU
KFENCE UAF / OOB(采样) 生产 <1% CPU
SLUB_DEBUG 对象损坏、double free 开发、定向排查 视选项 2–10×
kmemleak 内存泄漏 开发、长跑测试 10–20%

六、UBSAN / KCSAN

6.1 UBSAN

Undefined Behavior Sanitizer 的内核版:检测整数溢出、空指针解引用、对齐错误等 C 语言 UB。

CONFIG_UBSAN=y
CONFIG_UBSAN_TRAP=y    # 触发时 BUG() 而非只打日志

6.2 KCSAN

Kernel Concurrency Sanitizer:检测 data race(并发读写同一变量、无 READ_ONCE/WRITE_ONCE/锁等同步标记)。

CONFIG_KCSAN=y

报告示例:

BUG: KCSAN: data-race in func_a / func_b
  write to 0xffff... of 4 bytes by task A
  read to 0xffff... of 4 bytes by task B

修复:加锁、或标记 data_race() / READ_ONCE() / WRITE_ONCE()

七、实践工作流

7.1 开发 / CI

CONFIG_KASAN=y
CONFIG_KFENCE=y
CONFIG_DEBUG_KMEMLEAK=y
CONFIG_UBSAN=y
CONFIG_KCSAN=y
CONFIG_SLUB_DEBUG=y

跑 syzkaller / LTP,收集 dmesg 中的各类 report。CI 阶段以 KASAN + UBSAN 为主,KCSAN 单独跑(开销与误报都更敏感)。

7.2 生产

CONFIG_KFENCE=y          # 低开销采样
CONFIG_KASAN_HW_TAGS=y   # arm64 MTE 才有意义

其余不开。KFENCE report 通过 dmesg / syslog 收集,结合 crash dump 复盘。

八、常见问题

KASAN 太慢,不能开 generic 模式约 2–3× CPU 开销,确实只适合开发与 CI。arm64 上若硬件支持 MTE,可以切到 KASAN_HW_TAGS,开销降到 ~3–5%。x86 上生产侧只能依赖 KFENCE 采样。

kmemleak 误报多echo clear > /sys/kernel/debug/kmemleak,再触发负载、再 echo scan。把已知误报通过 kmemleak_not_leak() 注释到源码里,避免每次重复。

SLUB_DEBUG 想在生产开但不敢全开 使用 slub_debug=FZP,task_struct 这类参数,只对特定 cache 启用 redzone / poison / store_user,控制开销范围。

九、小结


参考文献

工具


上一篇per-CPU 变量 下一篇用户态分配器:jemalloc vs tcmalloc

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-05-03 · os

【操作系统百科】Slab/SLUB 分配器

buddy 只管页粒度(4K+),内核大多数对象只有几十到几百字节。slab/SLUB 在 buddy 之上做对象级缓存。本文讲 slab 历史、SLUB 接手、SLOB 退场、kmem_cache、per-CPU cache、KASAN 集成。

2026-05-08 · os

【操作系统百科】VFS 四层抽象

Linux 的一切皆文件靠 VFS 实现——superblock、inode、dentry、file 四层抽象加 ops 表。本文讲 VFS 核心数据结构、dcache、inode cache、RCU lookup,以及文件系统如何插入 VFS。

2026-04-22 · os

操作系统百科

Linux 6.x 视角下的操作系统系列索引:110 篇覆盖调度、虚拟内存、文件系统与 I/O、并发、隔离、可观测性,按主题、阅读路径与关键问题三种入口组织。


By .