用户态有 ASan/MSan/Valgrind;内核也有对等工具。KASAN 检测 UAF/OOB,KFENCE 在生产里低开销采样,kmemleak 抓泄漏。
一、先看图
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 检查。
- UAF(Use-After-Free):释放后 shadow 标记为”freed”
- OOB(Out-Of-Bounds):对象边界外 shadow 标记为”redzone”
启用: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 用统计采样:每隔一段时间(默认 100ms),把一个 slab 分配重定向到 guard page pool。
[guard page] [object] [guard page]
↑ ↑
不可访问 不可访问
OOB → 碰到 guard page → 页错误 → report。UAF → 释放后 guard page 仍在 → 再次访问 report。
3.2 特点
- 开销:<1% CPU,可跑生产
- 覆盖率:取决于采样率,不是每个 bug 都能抓到
- 无需特殊编译器
启用:CONFIG_KFENCE=y,默认已开(6.x
多数发行版)。
cat /sys/kernel/debug/kfence/stats
# enabled: 1
# objects: 255
# sample_interval: 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 局限
- 保守式:可能误报(整数恰好看起来像指针)
- 性能开销 ~10-20%
- 不适合生产
五、SLUB_DEBUG
E-42 提过。这里补充与其他工具的互补:
| 工具 | 检测 | 生产 | 开销 |
|---|---|---|---|
| KASAN generic | UAF/OOB/全覆盖 | ❌ | 2-3x |
| KASAN HW tag | UAF/OOB | ✅ (arm64 MTE) | 3-5% |
| KFENCE | UAF/OOB/采样 | ✅ | <1% |
| SLUB_DEBUG | corruption/double free | ❌ | 2-10x |
| kmemleak | leak | ❌ | 10-20% |
六、UBSAN / KCSAN
6.1 UBSAN
Undefined Behavior Sanitizer 的内核版:检测整数溢出、空指针解引用、对齐错误。
CONFIG_UBSAN=y
CONFIG_UBSAN_TRAP=y # 触发时 BUG() 而非只打日志
6.2 KCSAN
Kernel Concurrency Sanitizer:检测 data race(并发读写同一变量无同步)。
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_KMEMLEAK=y
CONFIG_UBSAN=y
CONFIG_KCSAN=y
CONFIG_SLUB_DEBUG=y
跑 syzkaller / LTP → 收集 report。
7.2 生产
CONFIG_KFENCE=y # 低开销采样
CONFIG_KASAN_HW_TAGS=y # arm64 MTE 有条件开
其余不开。KFENCE report 通过 dmesg / syslog
收集。
八、常见问题
A:KASAN 太慢 generic 模式确实 2-3x。arm64 有 MTE 可降到 3-5%。x86 只能用 KFENCE 替代。
B:kmemleak 误报多 清除后再 scan。排除 known false positive。
C:SLUB_DEBUG 生产不敢开 用
slub_debug=FZP,task_struct 只对特定 cache。
九、小结
- KASAN(Generic/SW/HW)是开发期 UAF/OOB 检测的金标
- KFENCE 是生产 UAF/OOB 的低开销采样
- kmemleak 抓泄漏,保守式 GC
- SLUB_DEBUG 检测对象损坏
- UBSAN/KCSAN 覆盖 UB 和 data race
- 组合使用覆盖面最广
参考文献
Documentation/dev-tools/kasan.rstDocumentation/dev-tools/kfence.rstDocumentation/dev-tools/kmemleak.rst- Alexander Potapenko, “KFENCE: A low-overhead sampling-based memory safety error detector.” 2021
- Dmitry Vyukov, “KCSAN: Kernel Concurrency Sanitizer.” 2019
工具
- dmesg(KASAN/KFENCE/SLUB_DEBUG/KCSAN 输出)
/sys/kernel/debug/kmemleak/sys/kernel/debug/kfence/stats- syzkaller
- crash / drgn
上一篇:per-CPU 变量 下一篇:用户态分配器
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【操作系统百科】Slab/SLUB 分配器
buddy 只管页粒度(4K+),内核大多数对象只有几十到几百字节。slab/SLUB 在 buddy 之上做对象级缓存。本文讲 slab 历史、SLUB 接手、SLOB 退场、kmem_cache、per-CPU cache、KASAN 集成。
【操作系统百科】lockdep 与内核锁验证
lockdep 报的 ABBA 死锁该不该怕?本文讲 lockdep 锁依赖追踪、lockdep_assert_held、KCSAN 并发检查、KFENCE 内存越界检测、false positive 处理。
【操作系统百科】内存回收
Linux 内存回收是 VM 最复杂的子系统之一。本文讲 active/inactive LRU、kswapd 与 direct reclaim、watermark 三线、swappiness 的真实含义、MGLRU 改造、memcg 回收与 PSI。
【操作系统百科】交换
swap 还值得开吗?本文讲 swap area 基础、swap cache、zram 压缩内存、zswap 前端压缩池、swappiness 的真实含义、容器里的 swap 策略,以及为什么现代 Android 全靠 zram 不靠磁盘。