scs: Add page accounting for shadow call stack allocations
[linux-block.git] / kernel / scs.c
CommitLineData
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
15static struct kmem_cache *scs_cache;
16
17static 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
34static void scs_free(void *s)
35{
36 kasan_unpoison_object_data(scs_cache, s);
37 kmem_cache_free(scs_cache, s);
38}
39
40void __init scs_init(void)
41{
42 scs_cache = kmem_cache_create("scs_cache", SCS_SIZE, 0, 0, NULL);
43}
44
628d06a4
ST
45static struct page *__scs_page(struct task_struct *tsk)
46{
47 return virt_to_page(task_scs(tsk));
48}
49
50static 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
56int 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
70void 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}