History

scs: switch to vmapped shadow stacks

The kernel currently uses kmem_cache to allocate shadow call stacks, which means an overflows may not be immediately detected and can potentially result in another task's shadow stack to be overwritten. This change switches SCS to use virtually mapped shadow stacks for tasks, which increases shadow stack size to a full page and provides more robust overflow detection, similarly to VMAP_STACK.

Sami Tolvanen committed 5 years ago

Commit a2abe7c1 parent f8394f22 files · +66 −17Browse files

Unified · Split

2 files
  1. include/linux/scs.h +6 −6
  2. kernel/scs.c +60 −11
include/linux/scs.h+6−6
@@ -15,12 +15,8 @@ · 14 unmodified lines
15 15
16#ifdef CONFIG_SHADOW_CALL_STACK16#ifdef CONFIG_SHADOW_CALL_STACK
17 17
18/*18#define SCS_ORDER 0
19 * In testing, 1 KiB shadow stack size (i.e. 128 stack frames on a 64-bit19#define SCS_SIZE (PAGE_SIZE << SCS_ORDER)
20 * architecture) provided ~40% safety margin on stack usage while keeping
21 * memory allocation overhead reasonable.
22 */
23#define SCS_SIZE SZ_1K
24#define GFP_SCS (GFP_KERNEL | __GFP_ZERO)20#define GFP_SCS (GFP_KERNEL | __GFP_ZERO)
25 21
26/* An illegal pointer value to mark the end of the shadow stack. */22/* An illegal pointer value to mark the end of the shadow stack. */
@@ -33,6 +29,8 @@ · 6 unmodified lines
33#define task_scs(tsk) (task_thread_info(tsk)->scs_base)29#define task_scs(tsk) (task_thread_info(tsk)->scs_base)
34#define task_scs_sp(tsk) (task_thread_info(tsk)->scs_sp)30#define task_scs_sp(tsk) (task_thread_info(tsk)->scs_sp)
35 31
32void *scs_alloc(int node);
33void scs_free(void *s);
36void scs_init(void);34void scs_init(void);
37int scs_prepare(struct task_struct *tsk, int node);35int scs_prepare(struct task_struct *tsk, int node);
38void scs_release(struct task_struct *tsk);36void scs_release(struct task_struct *tsk);
@@ -61,6 +59,8 @@ static inline bool task_scs_end_corrupted(struct task_struct *tsk) · 22 unmodified lines
61 59
62#else /* CONFIG_SHADOW_CALL_STACK */60#else /* CONFIG_SHADOW_CALL_STACK */
63 61
62static inline void *scs_alloc(int node) { return NULL; }
63static inline void scs_free(void *s) {}
64static inline void scs_init(void) {}64static inline void scs_init(void) {}
65static inline void scs_task_reset(struct task_struct *tsk) {}65static inline void scs_task_reset(struct task_struct *tsk) {}
66static inline int scs_prepare(struct task_struct *tsk, int node) { return 0; }66static inline int scs_prepare(struct task_struct *tsk, int node) { return 0; }
kernel/scs.c+60−11
@@ -5,26 +5,49 @@ · 4 unmodified lines
5 * Copyright (C) 2019 Google LLC5 * Copyright (C) 2019 Google LLC
6 */6 */
7 7
8#include <linux/cpuhotplug.h>
8#include <linux/kasan.h>9#include <linux/kasan.h>
9#include <linux/mm.h>10#include <linux/mm.h>
10#include <linux/scs.h>11#include <linux/scs.h>
11#include <linux/slab.h>12#include <linux/vmalloc.h>
12#include <linux/vmstat.h>13#include <linux/vmstat.h>
13 14
14static struct kmem_cache *scs_cache;
15
16static void __scs_account(void *s, int account)15static void __scs_account(void *s, int account)
17{16{
18 struct page *scs_page = virt_to_page(s);17 struct page *scs_page = vmalloc_to_page(s);
19 18
20 mod_node_page_state(page_pgdat(scs_page), NR_KERNEL_SCS_KB,19 mod_node_page_state(page_pgdat(scs_page), NR_KERNEL_SCS_KB,
21 account * (SCS_SIZE / SZ_1K));20 account * (SCS_SIZE / SZ_1K));
22}21}
23 22
24static void *scs_alloc(int node)23/* Matches NR_CACHED_STACKS for VMAP_STACK */
24#define NR_CACHED_SCS 2
25static DEFINE_PER_CPU(void *, scs_cache[NR_CACHED_SCS]);
26
27static void *__scs_alloc(int node)
25{28{
26 void *s = kmem_cache_alloc_node(scs_cache, GFP_SCS, node);29 int i;
30 void *s;
31
32 for (i = 0; i < NR_CACHED_SCS; i++) {
33 s = this_cpu_xchg(scs_cache[i], NULL);
34 if (s) {
35 kasan_unpoison_vmalloc(s, SCS_SIZE);
36 memset(s, 0, SCS_SIZE);
37 return s;
38 }
39 }
40
41 return __vmalloc_node_range(SCS_SIZE, 1, VMALLOC_START, VMALLOC_END,
42 GFP_SCS, PAGE_KERNEL, 0, node,
43 __builtin_return_address(0));
44}
27 45
46void *scs_alloc(int node)
47{
48 void *s;
49
50 s = __scs_alloc(node);
28 if (!s)51 if (!s)
29 return NULL;52 return NULL;
30 53
@@ -34,21 +57,47 @@ static void *scs_alloc(int node) · 3 unmodified lines
34 * Poison the allocation to catch unintentional accesses to57 * Poison the allocation to catch unintentional accesses to
35 * the shadow stack when KASAN is enabled.58 * the shadow stack when KASAN is enabled.
36 */59 */
37 kasan_poison_object_data(scs_cache, s);60 kasan_poison_vmalloc(s, SCS_SIZE);
38 __scs_account(s, 1);61 __scs_account(s, 1);
39 return s;62 return s;
40}63}
41 64
42static void scs_free(void *s)65void scs_free(void *s)
43{66{
67 int i;
68
44 __scs_account(s, -1);69 __scs_account(s, -1);
45 kasan_unpoison_object_data(scs_cache, s);70
46 kmem_cache_free(scs_cache, s);71 /*
72 * We cannot sleep as this can be called in interrupt context,
73 * so use this_cpu_cmpxchg to update the cache, and vfree_atomic
74 * to free the stack.
75 */
76
77 for (i = 0; i < NR_CACHED_SCS; i++)
78 if (this_cpu_cmpxchg(scs_cache[i], 0, s) == NULL)
79 return;
80
81 vfree_atomic(s);
82}
83
84static int scs_cleanup(unsigned int cpu)
85{
86 int i;
87 void **cache = per_cpu_ptr(scs_cache, cpu);
88
89 for (i = 0; i < NR_CACHED_SCS; i++) {
90 vfree(cache[i]);
91 cache[i] = NULL;
92 }
93
94 return 0;
47}95}
48 96
49void __init scs_init(void)97void __init scs_init(void)
50{98{
51 scs_cache = kmem_cache_create("scs_cache", SCS_SIZE, 0, 0, NULL);99 cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "scs:scs_cache", NULL,
100 scs_cleanup);
52}101}
53 102
54int scs_prepare(struct task_struct *tsk, int node)103int scs_prepare(struct task_struct *tsk, int node)

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t