这是<Linux内核内存管理>系列的第六篇
第一篇为内核内存管理过程知识点的的简单梳理
第二篇介绍了内核的数据结构
第三篇介绍了从内核第一行代码加载到跳转到C代码前的内存处理。
第四篇概览了初始化C代码中的内存处理
前言
Kernel Electric-Fence (KFENCE)是5.12版本内核新引入的内存使用错误检测机制。它可以检查的错误有:
- 内存访问越界
- 释放后使用
- 无效释放
显然,它可以检测的内存错误类型不如KASAN多。但与KASAN相比,它最大的优势是运行时小Overhead,可以直接用在生产环境中。因此在X86,ARM64,RISCV等平台上均默认开启。
在Arch对应的defconfig中使用CONFIG_HAVE_ARCH_KFENCE开启。
架构及原理
Kfence的原理比较简单,如下图:
初始化
- 初始化过程中,KFENCE向Memblock申请一段内存,作为KFENCE内存池。
- 这个内存池的大小配置为CONFIG_KFENCE_NUM_OBJECTS
- 即,预留两个页面作为保护页(Guard Page),接着为每一个用于分配的内存页分配一个Guard Page。因此总大小为:
1 |
- 初始化一个Delayed Worker,定期(CONFIG_KFENCE_SAMPLE_INTEVAL)重置kfence_alloc_gate值为0。
这个值可以通过sysfs修改
分配
- kfence_alloc_gate值为0时,使用kmem_cache_alloc所作的内存分配从KFENCE内存池中分配,并增加kfence_alloc_gate的值。kfence_alloc_gate值大于等于1时,直接从SLUB中分配。由此可以看出,kfence是基于采样的内存检测。
大于一个Page(4K)的分配不会从KFENCE Pool中分配
- 每次通过KFENCE进行内存分配时,都会从KFENCE内存池分配一个内存页和一个Guard Page,并在实际使用内存的两端内存填充Canary数据。
解释一下为什么保护数据叫Canary。这是因为在19世纪,金丝雀在采矿业中常用的毒气检测方法,因为它们比人类对毒气更为敏感反应也更快。
- 如果KFENCE内存池中没有可用内存,则直接从SLAB中分配。
释放
- 释放时,检查Canary数据,将所用内存放回KFENCE内存池。
检测报错
在以下情况,会检测报错:
- 释放时发现Canary数据不对。
- 当KFENCE内存池的内存区域发生Page Fault时,它或者是因为越界访问、或者是释放后使用。
- 无效释放:当一段KFENCE内存没有被标记分配,但对齐释放时,会有相应报错提示。
总结
开源社区总能带来新的idea。KFENCE,克服了KASAN等工具需要占用大量内存且影响运行时性能的缺点,是一个有效地运行时内存访问错误检测工具。
当然,因为它所针对的内存区域仅仅是KFENCE内存池,且其是周期性进行采样,检测效果还不得而知。其又有可以动态开关、参数可调节等优点,这些劣势或许也不是问题。后续若有时间可以研究分析对比其和KASAN的检测效果。