Commit | Line | Data |
---|---|---|
d08b9f0c ST |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Shadow Call Stack support. | |
4 | * | |
5 | * Copyright (C) 2019 Google LLC | |
6 | */ | |
7 | ||
8 | #include <linux/kasan.h> | |
628d06a4 | 9 | #include <linux/mm.h> |
d08b9f0c ST |
10 | #include <linux/scs.h> |
11 | #include <linux/slab.h> | |
628d06a4 | 12 | #include <linux/vmstat.h> |
d08b9f0c ST |
13 | #include <asm/scs.h> |
14 | ||
15 | static struct kmem_cache *scs_cache; | |
16 | ||
17 | static void *scs_alloc(int node) | |
18 | { | |
19 | void *s; | |
20 | ||
21 | s = kmem_cache_alloc_node(scs_cache, GFP_SCS, node); | |
22 | if (s) { | |
23 | *__scs_magic(s) = SCS_END_MAGIC; | |
24 | /* | |
25 | * Poison the allocation to catch unintentional accesses to | |
26 | * the shadow stack when KASAN is enabled. | |
27 | */ | |
28 | kasan_poison_object_data(scs_cache, s); | |
29 | } | |
30 | ||
31 | return s; | |
32 | } | |
33 | ||
34 | static void scs_free(void *s) | |
35 | { | |
36 | kasan_unpoison_object_data(scs_cache, s); | |
37 | kmem_cache_free(scs_cache, s); | |
38 | } | |
39 | ||
40 | void __init scs_init(void) | |
41 | { | |
42 | scs_cache = kmem_cache_create("scs_cache", SCS_SIZE, 0, 0, NULL); | |
43 | } | |
44 | ||
628d06a4 ST |
45 | static struct page *__scs_page(struct task_struct *tsk) |
46 | { | |
47 | return virt_to_page(task_scs(tsk)); | |
48 | } | |
49 | ||
50 | static void scs_account(struct task_struct *tsk, int account) | |
51 | { | |
52 | mod_zone_page_state(page_zone(__scs_page(tsk)), NR_KERNEL_SCS_KB, | |
53 | account * (SCS_SIZE / 1024)); | |
54 | } | |
55 | ||
d08b9f0c ST |
56 | int scs_prepare(struct task_struct *tsk, int node) |
57 | { | |
58 | void *s = scs_alloc(node); | |
59 | ||
60 | if (!s) | |
61 | return -ENOMEM; | |
62 | ||
63 | task_scs(tsk) = s; | |
64 | task_scs_offset(tsk) = 0; | |
628d06a4 | 65 | scs_account(tsk, 1); |
d08b9f0c ST |
66 | |
67 | return 0; | |
68 | } | |
69 | ||
70 | void scs_release(struct task_struct *tsk) | |
71 | { | |
72 | void *s = task_scs(tsk); | |
73 | ||
74 | if (!s) | |
75 | return; | |
76 | ||
77 | WARN(scs_corrupted(tsk), "corrupted shadow stack detected when freeing task\n"); | |
628d06a4 | 78 | scs_account(tsk, -1); |
d08b9f0c ST |
79 | scs_free(s); |
80 | } |