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 边界,就会立刻触发异常。
-
+
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。
-
+
-### 1.2 out-of-bounds (over data page)
+### out-of-bounds (over data page)
从 `kfence_pool` 中成功分配一个内存对象 `obj`,不管 `obj` 的实际大小有多大,都会占据一个 `data page`。
-
+
当原本访问 `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` 范围内的溢出访问。
-
+
这种类型的溢出是不能在溢出发生时立刻触发的,它只能在 `obj` free 时,通过检测 `canary pattern` 被破坏来检测到有 `canary` 区域的溢出访问。但是异常访问的元凶却不能直接抓出来。
-### 1.4 use-after-free
+### use-after-free
在 `obj` 被 free 以后,对应 `data page` 也会被设置成不可访问状态。
-
+
这种状态下,如果有操作继续访问 `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