author: 熊潇 of IceSword Lab
本文研究了内核编译选项 CONFIG_SLAB_MERGE_DEFAULT
对 kmem_cache
分配的影响.
以及开启该配置的时候, slab UAF 的一种利用方案 (方案来源, 本文内容基于 Linux-5.10.90).
阅读前, 需要对 slab/slub, Buddy system 有基本的了解.
- Part. 1: 源码分析
- Part. 2:
CONFIG_SLAB_MERGE_DEFAULT
配置对比测试 - Part. 3: 跨 slab 的 UAF 利用示例
Keyword: slab/slub | CONFIG_SLAB_MERGE_DEFAULT | Linux kernel exploit
Part. 1
创建 struct kmem_cache
的时候,有两种情况:
__kmem_cache_alias
: 跟现有的共用(mergeable)create_cache
: 创建一个新的
1 | kmem_cache_create(..) |
Part.2
测试 CONFIG_SLAB_MERGE_DEFAULT
的影响
Host 主机(开启了配置):
1 | └─[$] uname -r |
VM (未开启配置):
1 | ➜ ~ uname -r |
code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
struct my_struct {
char data[OBJ_SIZE];
};
static struct kmem_cache *my_cachep;
static struct my_struct *ms[OBJ_NUM];
static int __init km_init(void){
int i, cpu;
struct kmem_cache_cpu *c;
struct page *pg;
pr_info("Hello\n");
my_cachep = kmem_cache_create("my_struct",
sizeof(struct my_struct), 0,
SLAB_HWCACHE_ALIGN | SLAB_PANIC | SLAB_ACCOUNT,
NULL);
pr_info("my_cachep: %px, %s\n", my_cachep, my_cachep->name);
pr_info("my_cachep.size: %u\n", my_cachep->size);
pr_info("my_cachep.object_size: %u\n", kmem_cache_size(my_cachep));
cpu = get_cpu();
pr_info("cpu: %d\n", cpu);
c = per_cpu_ptr(my_cachep->cpu_slab, cpu);
for(i = 0; i<OBJ_NUM; i++){
ms[i] = kmem_cache_alloc(my_cachep, GFP_KERNEL);
pg = virt_to_page(ms[i]);
pr_info("[%02d] object: %px, page: %px(%px), %d\n", i, ms[i],
pg, page_address(pg),
(void *)pg == (void *)c->page);
}
return 0;
}
static void __exit km_exit(void)
{
int i;
for( i = 0; i<OBJ_NUM; i++){
kmem_cache_free(my_cachep, ms[i]);
}
kmem_cache_destroy(my_cachep);
pr_info("Bye\n");
}
module_init(km_init);
module_exit(km_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("X++D");
MODULE_DESCRIPTION("Kernel xxx Module.");
MODULE_VERSION("0.1");VM result
分配的 object 地址和 page 的关系非常清晰
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54➜ ~ insmod slab-tc.ko
[ 1184.983757] Hello
[ 1184.984278] my_cachep: ffff8880096ea000, my_struct
[ 1184.985568] my_cachep.size: 256
[ 1184.986451] my_cachep.object_size: 256
[ 1184.987488] cpu: 0
**[ 1184.988945] [00] object: ffff888005c38000, page: ffffea0000170e00(ffff888005c38000), 1**
[ 1184.991189] [01] object: ffff888005c38100, page: ffffea0000170e00(ffff888005c38000), 1
[ 1184.993438] [02] object: ffff888005c38200, page: ffffea0000170e00(ffff888005c38000), 1
[ 1184.995688] [03] object: ffff888005c38300, page: ffffea0000170e00(ffff888005c38000), 1
[ 1184.998018] [04] object: ffff888005c38400, page: ffffea0000170e00(ffff888005c38000), 1
[ 1185.000234] [05] object: ffff888005c38500, page: ffffea0000170e00(ffff888005c38000), 1
[ 1185.002529] [06] object: ffff888005c38600, page: ffffea0000170e00(ffff888005c38000), 1
[ 1185.004702] [07] object: ffff888005c38700, page: ffffea0000170e00(ffff888005c38000), 1
[ 1185.006841] [08] object: ffff888005c38800, page: ffffea0000170e00(ffff888005c38000), 1
[ 1185.008919] [09] object: ffff888005c38900, page: ffffea0000170e00(ffff888005c38000), 1
[ 1185.010944] [10] object: ffff888005c38a00, page: ffffea0000170e00(ffff888005c38000), 1
[ 1185.013021] [11] object: ffff888005c38b00, page: ffffea0000170e00(ffff888005c38000), 1
[ 1185.014904] [12] object: ffff888005c38c00, page: ffffea0000170e00(ffff888005c38000), 1
[ 1185.016926] [13] object: ffff888005c38d00, page: ffffea0000170e00(ffff888005c38000), 1
[ 1185.018883] [14] object: ffff888005c38e00, page: ffffea0000170e00(ffff888005c38000), 1
**[ 1185.020761] [15] object: ffff888005c38f00, page: ffffea0000170e00(ffff888005c38000), 1**
**[ 1185.022735] [16] object: ffff88800953d000, page: ffffea0000254f40(ffff88800953d000), 1**
[ 1185.024679] [17] object: ffff88800953d100, page: ffffea0000254f40(ffff88800953d000), 1
[ 1185.026579] [18] object: ffff88800953d200, page: ffffea0000254f40(ffff88800953d000), 1
[ 1185.028528] [19] object: ffff88800953d300, page: ffffea0000254f40(ffff88800953d000), 1
[ 1185.030443] [20] object: ffff88800953d400, page: ffffea0000254f40(ffff88800953d000), 1
[ 1185.032372] [21] object: ffff88800953d500, page: ffffea0000254f40(ffff88800953d000), 1
[ 1185.034263] [22] object: ffff88800953d600, page: ffffea0000254f40(ffff88800953d000), 1
[ 1185.036116] [23] object: ffff88800953d700, page: ffffea0000254f40(ffff88800953d000), 1
[ 1185.038086] [24] object: ffff88800953d800, page: ffffea0000254f40(ffff88800953d000), 1
[ 1185.039929] [25] object: ffff88800953d900, page: ffffea0000254f40(ffff88800953d000), 1
[ 1185.041944] [26] object: ffff88800953da00, page: ffffea0000254f40(ffff88800953d000), 1
[ 1185.043852] [27] object: ffff88800953db00, page: ffffea0000254f40(ffff88800953d000), 1
[ 1185.045736] [28] object: ffff88800953dc00, page: ffffea0000254f40(ffff88800953d000), 1
[ 1185.047678] [29] object: ffff88800953dd00, page: ffffea0000254f40(ffff88800953d000), 1
[ 1185.049585] [30] object: ffff88800953de00, page: ffffea0000254f40(ffff88800953d000), 1
**[ 1185.051391] [31] object: ffff88800953df00, page: ffffea0000254f40(ffff88800953d000), 1**
**[ 1185.053206] [32] object: ffff888009543000, page: ffffea00002550c0(ffff888009543000), 1**
[ 1185.055038] [33] object: ffff888009543100, page: ffffea00002550c0(ffff888009543000), 1
[ 1185.056666] [34] object: ffff888009543200, page: ffffea00002550c0(ffff888009543000), 1
[ 1185.058430] [35] object: ffff888009543300, page: ffffea00002550c0(ffff888009543000), 1
[ 1185.060174] [36] object: ffff888009543400, page: ffffea00002550c0(ffff888009543000), 1
[ 1185.061955] [37] object: ffff888009543500, page: ffffea00002550c0(ffff888009543000), 1
[ 1185.063694] [38] object: ffff888009543600, page: ffffea00002550c0(ffff888009543000), 1
[ 1185.065468] [39] object: ffff888009543700, page: ffffea00002550c0(ffff888009543000), 1
[ 1185.067231] [40] object: ffff888009543800, page: ffffea00002550c0(ffff888009543000), 1
[ 1185.068930] [41] object: ffff888009543900, page: ffffea00002550c0(ffff888009543000), 1
[ 1185.070600] [42] object: ffff888009543a00, page: ffffea00002550c0(ffff888009543000), 1
[ 1185.072224] [43] object: ffff888009543b00, page: ffffea00002550c0(ffff888009543000), 1
[ 1185.073911] [44] object: ffff888009543c00, page: ffffea00002550c0(ffff888009543000), 1
[ 1185.075534] [45] object: ffff888009543d00, page: ffffea00002550c0(ffff888009543000), 1
[ 1185.077211] [46] object: ffff888009543e00, page: ffffea00002550c0(ffff888009543000), 1
**[ 1185.078887] [47] object: ffff888009543f00, page: ffffea00002550c0(ffff888009543000), 1**有独立的 sysfs 目录
1
2
3
4
5➜ ~ file /sys/kernel/slab/my_struct
/sys/kernel/slab/my_struct: directory
➜ ~ file /sys/kernel/slab/pool_workqueue
/sys/kernel/slab/pool_workqueue: directoryHost result
分配的 obj 位于的 page 地址非常杂乱,
my_cachep
的name
也变成了pool_workqueue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54[435532.063645] Hello
[435532.063655] my_cachep: ffff8faf40045900, pool_workqueue
[435532.063658] my_cachep.size: 256
[435532.063659] my_cachep.object_size: 256
[435532.063660] cpu: 0
[435532.063662] [00] object: ffff8fafb100b400, page: ffffd50545c402c0(ffff8fafb100b000), 0
[435532.063664] [01] object: ffff8fafb100a700, page: ffffd50545c40280(ffff8fafb100a000), 1
[435532.063666] [02] object: ffff8fafb100ae00, page: ffffd50545c40280(ffff8fafb100a000), 1
[435532.063668] [03] object: ffff8fafb100b900, page: ffffd50545c402c0(ffff8fafb100b000), 0
[435532.063670] [04] object: ffff8fafb100be00, page: ffffd50545c402c0(ffff8fafb100b000), 0
[435532.063672] [05] object: ffff8fafb100bf00, page: ffffd50545c402c0(ffff8fafb100b000), 0
[435532.063674] [06] object: ffff8fafb100af00, page: ffffd50545c40280(ffff8fafb100a000), 1
[435532.063676] [07] object: ffff8fafb100ad00, page: ffffd50545c40280(ffff8fafb100a000), 1
[435532.063677] [08] object: ffff8fafb100bc00, page: ffffd50545c402c0(ffff8fafb100b000), 0
[435532.063679] [09] object: ffff8fafb100a600, page: ffffd50545c40280(ffff8fafb100a000), 1
[435532.063681] [10] object: ffff8fafb100a800, page: ffffd50545c40280(ffff8fafb100a000), 1
[435532.063683] [11] object: ffff8fafb100a000, page: ffffd50545c40280(ffff8fafb100a000), 1
[435532.063685] [12] object: ffff8fafb100ab00, page: ffffd50545c40280(ffff8fafb100a000), 1
[435532.063687] [13] object: ffff8fafb100b300, page: ffffd50545c402c0(ffff8fafb100b000), 0
[435532.063689] [14] object: ffff8fafb100a900, page: ffffd50545c40280(ffff8fafb100a000), 1
[435532.063690] [15] object: ffff8fafb100b000, page: ffffd50545c402c0(ffff8fafb100b000), 0
[435532.063692] [16] object: ffff8fafb100a100, page: ffffd50545c40280(ffff8fafb100a000), 1
[435532.063694] [17] object: ffff8fafb100b100, page: ffffd50545c402c0(ffff8fafb100b000), 0
[435532.063696] [18] object: ffff8fafb100b500, page: ffffd50545c402c0(ffff8fafb100b000), 0
[435532.063698] [19] object: ffff8fafb100bd00, page: ffffd50545c402c0(ffff8fafb100b000), 0
[435532.063700] [20] object: ffff8fafb100ba00, page: ffffd50545c402c0(ffff8fafb100b000), 0
[435532.063702] [21] object: ffff8fafb100b700, page: ffffd50545c402c0(ffff8fafb100b000), 0
[435532.063703] [22] object: ffff8fafb100a200, page: ffffd50545c40280(ffff8fafb100a000), 1
[435532.063705] [23] object: ffff8fafb100b200, page: ffffd50545c402c0(ffff8fafb100b000), 0
[435532.063707] [24] object: ffff8fafb100bb00, page: ffffd50545c402c0(ffff8fafb100b000), 0
[435532.063709] [25] object: ffff8fafb100aa00, page: ffffd50545c40280(ffff8fafb100a000), 1
[435532.063711] [26] object: ffff8fafb100a500, page: ffffd50545c40280(ffff8fafb100a000), 1
[435532.063713] [27] object: ffff8fafb100b600, page: ffffd50545c402c0(ffff8fafb100b000), 0
[435532.063714] [28] object: ffff8fafb100b800, page: ffffd50545c402c0(ffff8fafb100b000), 0
[435532.063716] [29] object: ffff8fafb100a400, page: ffffd50545c40280(ffff8fafb100a000), 1
[435532.063718] [30] object: ffff8fafb100ac00, page: ffffd50545c40280(ffff8fafb100a000), 1
[435532.063720] [31] object: ffff8fafb100a300, page: ffffd50545c40280(ffff8fafb100a000), 1
[435532.063724] [32] object: ffff8faf488fec00, page: ffffd50544223f80(ffff8faf488fe000), 1
[435532.063726] [33] object: ffff8faf488fe400, page: ffffd50544223f80(ffff8faf488fe000), 1
[435532.063728] [34] object: ffff8faf488ff800, page: ffffd50544223fc0(ffff8faf488ff000), 0
[435532.063730] [35] object: ffff8faf488ff600, page: ffffd50544223fc0(ffff8faf488ff000), 0
[435532.063732] [36] object: ffff8faf488fe500, page: ffffd50544223f80(ffff8faf488fe000), 1
[435532.063734] [37] object: ffff8faf488fea00, page: ffffd50544223f80(ffff8faf488fe000), 1
[435532.063736] [38] object: ffff8faf488ffb00, page: ffffd50544223fc0(ffff8faf488ff000), 0
[435532.063737] [39] object: ffff8faf488ff200, page: ffffd50544223fc0(ffff8faf488ff000), 0
[435532.063739] [40] object: ffff8faf488fe200, page: ffffd50544223f80(ffff8faf488fe000), 1
[435532.063741] [41] object: ffff8faf488ff700, page: ffffd50544223fc0(ffff8faf488ff000), 0
[435532.063743] [42] object: ffff8faf488ffa00, page: ffffd50544223fc0(ffff8faf488ff000), 0
[435532.063745] [43] object: ffff8faf488ff400, page: ffffd50544223fc0(ffff8faf488ff000), 0
[435532.063747] [44] object: ffff8faf488fe700, page: ffffd50544223f80(ffff8faf488fe000), 1
[435532.063749] [45] object: ffff8faf488fee00, page: ffffd50544223f80(ffff8faf488fe000), 1
[435532.063750] [46] object: ffff8faf488ff900, page: ffffd50544223fc0(ffff8faf488ff000), 0
[435532.063752] [47] object: ffff8faf488ffe00, page: ffffd50544223fc0(ffff8faf488ff000), 0
[435532.065672] Byesysfs 目录也是和
pool_workqueue
共用的1
2
3
4
5└─[$] file /sys/kernel/slab/my_struct
/sys/kernel/slab/my_struct: symbolic link to :0000256
└─[$] file /sys/kernel/slab/pool_workqueue
/sys/kernel/slab/pool_workqueue: symbolic link to :0000256
Part. 3
根据前两个部分知道,开启 CONFIG_SLAB_MERGE_DEFAULT
配置后,不同类型的 kmem_cache
的内存完全隔离.
这种情况下,想要占据被释放的 slab object 内存(比如一个 struct file
) 只能通过申请相同的 slab object,
而像 struct file
这样的内存,用户态可以操纵的内容非常有限,
解决办法是: 占据目标 object (e.g. struct file
) 所在的整个 page,在 object invalid free 之后 free 掉同页面其他 object,再满足一系列条件 就可以让整个 page 被 buddy system 回收,并被重新申请
条件一:
目标 object 所在的 page 不是 s->cpu_slab->page
1 | static __always_inline void do_slab_free(struct kmem_cache *s, |
条件二:
object 所在 page 满足 page->pobjects > (s)->cpu_partial
1 | // #define slub_cpu_partial(s) ((s)->cpu_partial) |
条件三:
object 所在 page 位于 freelist
且 page.inuse
为 0
1 | static void unfreeze_partials(struct kmem_cache *s, |
触发方法:
- 创建一批 objects 占满 cpu_partial + 2 个 pages, 保证 free 的时候
page->pobjects > (s)->cpu_partial
- 创建 objects 占据一个新的 page ,但不占满,保证
c->page
指向这个 page - free 掉一个 page 的所有 objects, 使这个 page 的
page.inuse == 0
- 剩下的每个 page free 一个 object 用完 partial list 后就会 free 掉目标 page
代码如下:
1 | /* |