1d08b9f0cSSami Tolvanen // SPDX-License-Identifier: GPL-2.0 2d08b9f0cSSami Tolvanen /* 3d08b9f0cSSami Tolvanen * Shadow Call Stack support. 4d08b9f0cSSami Tolvanen * 5d08b9f0cSSami Tolvanen * Copyright (C) 2019 Google LLC 6d08b9f0cSSami Tolvanen */ 7d08b9f0cSSami Tolvanen 8d08b9f0cSSami Tolvanen #include <linux/kasan.h> 9628d06a4SSami Tolvanen #include <linux/mm.h> 10d08b9f0cSSami Tolvanen #include <linux/scs.h> 11d08b9f0cSSami Tolvanen #include <linux/slab.h> 12628d06a4SSami Tolvanen #include <linux/vmstat.h> 13d08b9f0cSSami Tolvanen #include <asm/scs.h> 14d08b9f0cSSami Tolvanen 15d08b9f0cSSami Tolvanen static struct kmem_cache *scs_cache; 16d08b9f0cSSami Tolvanen 17d08b9f0cSSami Tolvanen static void *scs_alloc(int node) 18d08b9f0cSSami Tolvanen { 19d08b9f0cSSami Tolvanen void *s; 20d08b9f0cSSami Tolvanen 21d08b9f0cSSami Tolvanen s = kmem_cache_alloc_node(scs_cache, GFP_SCS, node); 22d08b9f0cSSami Tolvanen if (s) { 23d08b9f0cSSami Tolvanen *__scs_magic(s) = SCS_END_MAGIC; 24d08b9f0cSSami Tolvanen /* 25d08b9f0cSSami Tolvanen * Poison the allocation to catch unintentional accesses to 26d08b9f0cSSami Tolvanen * the shadow stack when KASAN is enabled. 27d08b9f0cSSami Tolvanen */ 28d08b9f0cSSami Tolvanen kasan_poison_object_data(scs_cache, s); 29d08b9f0cSSami Tolvanen } 30d08b9f0cSSami Tolvanen 31d08b9f0cSSami Tolvanen return s; 32d08b9f0cSSami Tolvanen } 33d08b9f0cSSami Tolvanen 34d08b9f0cSSami Tolvanen static void scs_free(void *s) 35d08b9f0cSSami Tolvanen { 36d08b9f0cSSami Tolvanen kasan_unpoison_object_data(scs_cache, s); 37d08b9f0cSSami Tolvanen kmem_cache_free(scs_cache, s); 38d08b9f0cSSami Tolvanen } 39d08b9f0cSSami Tolvanen 40d08b9f0cSSami Tolvanen void __init scs_init(void) 41d08b9f0cSSami Tolvanen { 42d08b9f0cSSami Tolvanen scs_cache = kmem_cache_create("scs_cache", SCS_SIZE, 0, 0, NULL); 43d08b9f0cSSami Tolvanen } 44d08b9f0cSSami Tolvanen 45628d06a4SSami Tolvanen static struct page *__scs_page(struct task_struct *tsk) 46628d06a4SSami Tolvanen { 47628d06a4SSami Tolvanen return virt_to_page(task_scs(tsk)); 48628d06a4SSami Tolvanen } 49628d06a4SSami Tolvanen 50628d06a4SSami Tolvanen static void scs_account(struct task_struct *tsk, int account) 51628d06a4SSami Tolvanen { 52628d06a4SSami Tolvanen mod_zone_page_state(page_zone(__scs_page(tsk)), NR_KERNEL_SCS_KB, 53628d06a4SSami Tolvanen account * (SCS_SIZE / 1024)); 54628d06a4SSami Tolvanen } 55628d06a4SSami Tolvanen 56d08b9f0cSSami Tolvanen int scs_prepare(struct task_struct *tsk, int node) 57d08b9f0cSSami Tolvanen { 58d08b9f0cSSami Tolvanen void *s = scs_alloc(node); 59d08b9f0cSSami Tolvanen 60d08b9f0cSSami Tolvanen if (!s) 61d08b9f0cSSami Tolvanen return -ENOMEM; 62d08b9f0cSSami Tolvanen 63d08b9f0cSSami Tolvanen task_scs(tsk) = s; 64d08b9f0cSSami Tolvanen task_scs_offset(tsk) = 0; 65628d06a4SSami Tolvanen scs_account(tsk, 1); 66d08b9f0cSSami Tolvanen return 0; 67d08b9f0cSSami Tolvanen } 68d08b9f0cSSami Tolvanen 69*5bbaf9d1SSami Tolvanen static void scs_check_usage(struct task_struct *tsk) 70*5bbaf9d1SSami Tolvanen { 71*5bbaf9d1SSami Tolvanen static unsigned long highest; 72*5bbaf9d1SSami Tolvanen 73*5bbaf9d1SSami Tolvanen unsigned long *p, prev, curr = highest, used = 0; 74*5bbaf9d1SSami Tolvanen 75*5bbaf9d1SSami Tolvanen if (!IS_ENABLED(CONFIG_DEBUG_STACK_USAGE)) 76*5bbaf9d1SSami Tolvanen return; 77*5bbaf9d1SSami Tolvanen 78*5bbaf9d1SSami Tolvanen for (p = task_scs(tsk); p < __scs_magic(tsk); ++p) { 79*5bbaf9d1SSami Tolvanen if (!READ_ONCE_NOCHECK(*p)) 80*5bbaf9d1SSami Tolvanen break; 81*5bbaf9d1SSami Tolvanen used++; 82*5bbaf9d1SSami Tolvanen } 83*5bbaf9d1SSami Tolvanen 84*5bbaf9d1SSami Tolvanen while (used > curr) { 85*5bbaf9d1SSami Tolvanen prev = cmpxchg_relaxed(&highest, curr, used); 86*5bbaf9d1SSami Tolvanen 87*5bbaf9d1SSami Tolvanen if (prev == curr) { 88*5bbaf9d1SSami Tolvanen pr_info("%s (%d): highest shadow stack usage: %lu bytes\n", 89*5bbaf9d1SSami Tolvanen tsk->comm, task_pid_nr(tsk), used); 90*5bbaf9d1SSami Tolvanen break; 91*5bbaf9d1SSami Tolvanen } 92*5bbaf9d1SSami Tolvanen 93*5bbaf9d1SSami Tolvanen curr = prev; 94*5bbaf9d1SSami Tolvanen } 95*5bbaf9d1SSami Tolvanen } 96*5bbaf9d1SSami Tolvanen 97d08b9f0cSSami Tolvanen void scs_release(struct task_struct *tsk) 98d08b9f0cSSami Tolvanen { 99d08b9f0cSSami Tolvanen void *s = task_scs(tsk); 100d08b9f0cSSami Tolvanen 101d08b9f0cSSami Tolvanen if (!s) 102d08b9f0cSSami Tolvanen return; 103d08b9f0cSSami Tolvanen 104d08b9f0cSSami Tolvanen WARN(scs_corrupted(tsk), "corrupted shadow stack detected when freeing task\n"); 105*5bbaf9d1SSami Tolvanen scs_check_usage(tsk); 106628d06a4SSami Tolvanen scs_account(tsk, -1); 107d08b9f0cSSami Tolvanen scs_free(s); 108d08b9f0cSSami Tolvanen } 109