diff --git a/articles/20220428-linux-kfence.md b/articles/20220428-linux-kfence.md index 4e6adcfcda3f8f06da8bf2597a6f14e986862465..2a8ccec0e2da05c36ef622a40f223dd69d9b0272 100644 --- a/articles/20220428-linux-kfence.md +++ b/articles/20220428-linux-kfence.md @@ -1,70 +1,72 @@ +> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.1-rc2 - [tables images urls pangu autocorrect]
> Author: pwl999
> Date: 2022/04/28
+> Revisor: Falcon
> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
> Sponsor: PLCT Lab, ISCAS # Kfence 详解 -> Author: pwl -> Date: 2022-04-28 +> Author: pwl +> Date: 2022-04-28 -## 1. 原理介绍 +## 原理介绍 Kfence (Kernel Electric Fence) 是 Linux 内核引入的一种低开销的内存错误检测机制,因为是低开销的所以它可以在运行的生产环境中开启,同样由于是低开销所以它的功能相比较 Kasan 会偏弱。 -Kfence 的基本原理非常简单,它创建了自己的专有检测内存池 `kfence_pool`。在 `data page` 的两边加上了 `fence page` 电子栅栏,利用 MMU 的特性把 `fence page` 设置成不可访问。如果对 `data page` 的访问越过了 page 边界, 就会立刻触发异常。 +Kfence 的基本原理非常简单,它创建了自己的专有检测内存池 `kfence_pool`。在 `data page` 的两边加上了 `fence page` 电子栅栏,利用 MMU 的特性把 `fence page` 设置成不可访问。如果对 `data page` 的访问越过了 page 边界,就会立刻触发异常。 -![](images/kfence/kfence_pool.png) +![kfence_pool.png](images/kfence/kfence_pool.png) Kfence 的主要特点如下: | item | Kfence | Kasan | -| -------- | ----------------------------------------- | ---------------------- | +|----------|-------------------------------------------|------------------------| | 检测密度 | 抽样法,默认每 100ms 提供一个可检测的内存 | 对所有内存访问进行检测 | | 检测粒度 | 核心的检测粒度为 page | 检测粒度为字节 | -### 1.1 slub/slab hook +### slub/slab hook Kfence 把自己 hook 到 `slub/slab` 的 `malloc()/free()` 流程当中去。但并不是所有的 `slub/slab` 内存都会从 `kfence_pool` 内存池中分配。它规定了两个条件: - 1、默认每隔 100 ms,开放从 `kfence_pool` 内存池中分配一次数据。分配成功后会把 `kfence_allocation_gate` 加 1,阻止继续从 `kfence_pool` 的分配。`kfence_timer` 定时到期以后,又会重新开放一次分配。这相当于一种 `抽样法`。 - 2、每次分配都会占用 `kfence_pool` 中的一个 `data page`,所以可分配的内存长度最大为 1 page。 -![](images/kfence/kfence_slub_hook.png) +![kfence_slub_hook.png](images/kfence/kfence_slub_hook.png) -### 1.2 out-of-bounds (over data page) +### out-of-bounds (over data page) 从 `kfence_pool` 中成功分配一个内存对象 `obj`,不管 `obj` 的实际大小有多大,都会占据一个 `data page`。 -![](images/kfence/kfence_outbound_fence.png) +![kfence_outbound_fence.png](images/kfence/kfence_outbound_fence.png) 当原本访问 `obj` 的操作溢出到相邻的 `fence page` 时,会立即触发 CPU 异常,通过堆栈回溯揪出异常访问的元凶。 -### 1.3 out-of-bounds (in data page) +### out-of-bounds (in data page) 大部分情况下 `obj` 是小于一个 page 的,对于 `data page` 剩余空间系统使用 `canary pattern` 进行填充。这种操作是为了检测超出了 `obj` 但还在 `data page` 范围内的溢出访问。 -![](images/kfence/kfence_outbound_canary.png) +![kfence_outbound_canary.png](images/kfence/kfence_outbound_canary.png) 这种类型的溢出是不能在溢出发生时立刻触发的,它只能在 `obj` free 时,通过检测 `canary pattern` 被破坏来检测到有 `canary` 区域的溢出访问。但是异常访问的元凶却不能直接抓出来。 -### 1.4 use-after-free +### use-after-free 在 `obj` 被 free 以后,对应 `data page` 也会被设置成不可访问状态。 -![](images/kfence/kfence_use_afterfree.png) +![kfence_use_afterfree.png](images/kfence/kfence_use_afterfree.png) 这种状态下,如果有操作继续访问 `obj` 会立即触发 CPU 异常,通过堆栈回溯揪出异常访问的元凶。 -### 1.5 invalid-free +### invalid-free 在 `obj` free 时会判断记录的 malloc 信息,判断是不是一次异常的 free。 -## 2. 代码解析 +## 代码解析 分析以下关键的代码流程: -### 2.1 kfence_protect() +### kfence_protect() 把 `fence page` 设置成不可访问的核心就是通过 MMU 清除掉 PTE 中的 `present` 标志位: @@ -89,7 +91,7 @@ static inline bool kfence_protect_page(unsigned long addr, bool protect) } ``` -### 2.2 kfence_alloc_pool() +### kfence_alloc_pool() 在系统启动时保留 Kfence 需要用到的内存 Page,默认保留 255 个 `data page`: @@ -115,7 +117,7 @@ config KFENCE_NUM_OBJECTS default 255 ``` -### 2.3 kfence_init() +### kfence_init() ``` void __init kfence_init(void) @@ -142,7 +144,7 @@ void __init kfence_init(void) } ``` -### 2.4 kfence_alloc() +### kfence_alloc() 内存分配流程: @@ -150,7 +152,7 @@ void __init kfence_init(void) kmem_cache_alloc() → slab_alloc() → kfence_alloc() → __kfence_alloc() → kfence_guarded_alloc(): ``` -### 2.5 kfence_free() +### kfence_free() 内存释放流程: @@ -160,7 +162,12 @@ kfence_free() → __kfence_free() → kfence_guarded_free(): ## 参考文档 -1.[Linux内存异常检测工具—kfence](https://www.jianshu.com/p/f967086f9129) -2.[Kernel Electric-Fence (KFENCE)](https://www.kernel.org/doc/html/latest/dev-tools/kfence.html) -3.[Linux Kernel Sanitizers](https://gitee.com/mirrors/KASAN) -4.[Linux开源动态之一种新的内存非法访问检查工具KFence](https://www.cnblogs.com/liuhailong0112/p/14683431.html) +1. [Linux 内存异常检测工具—kfence][003] +2. [Kernel Electric-Fence (KFENCE)][004] +3. [Linux Kernel Sanitizers][001] +4. [Linux 开源动态之一种新的内存非法访问检查工具 KFence][002] + +[001]: https://gitee.com/mirrors/KASAN +[002]: https://www.cnblogs.com/liuhailong0112/p/14683431.html +[003]: https://www.jianshu.com/p/f967086f9129 +[004]: https://www.kernel.org/doc/html/latest/dev-tools/kfence.html