Home Kernel Electric Fence (kfence)
Post
Cancel

Kernel Electric Fence (kfence)

一、kfence

Kfence (Kernel Electric Fence) 是 Linux 内核引入的一种低开销的内存错误检测机制,因为是低开销的所以它可以在运行的生产环境中开启,同样由于是低开销所以它的功能相比较 Kasan 会偏弱。

Kfence 的基本原理非常简单,它创建了自己的专有检测内存池 kfence_pool。在 data page 的两边加上了 fence page 电子栅栏,利用 MMU 的特性把 fence page 设置成不可访问。如果对 data page 的访问越过了 page 边界, 就会立刻触发异常。

Kfence 的主要特点如下:

item Kfence Kasan
检测密度 抽样法,默认每 100ms 提供一个可检测的内存 对所有内存访问进行检测
检测粒度 核心的检测粒度为 page 检测粒度为字节

Kernel Electric-Fence (KFENCE)是5.12版本内核新引入的内存使用错误检测机制。它可以检查的错误有:

  • 内存访问越界
  • 释放后使用
  • 无效释放

显然,它可以检测的内存错误类型不如KASAN多。但与KASAN相比,它最大的优势是运行时小Overhead,可以直接用在生产环境中。因此在X86,ARM64,RISCV等平台上均默认开启。

在Arch对应的defconfig中使用CONFIG_HAVE_ARCH_KFENCE开启。

架构及原理

Kfence的原理比较简单,如下图:

img

初始化

  • 初始化过程中,KFENCE向Memblock申请一段内存,作为KFENCE内存池。
  • 这个内存池的大小配置为CONFIG_KFENCE_NUM_OBJECTS
  • 即,预留两个页面作为保护页(Guard Page),接着为每一个用于分配的内存页分配一个Guard Page。因此总大小为:
1
#define KFENCE_POOL_SIZE ((CONFIG_KFENCE_NUM_OBJECTS + 1) * 2 * PAGE_SIZE)
  • 初始化一个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的检测效果。

This post is licensed under CC BY 4.0 by the author.