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 #ifndef _LINUX_SCS_H 9d08b9f0cSSami Tolvanen #define _LINUX_SCS_H 10d08b9f0cSSami Tolvanen 11d08b9f0cSSami Tolvanen #include <linux/gfp.h> 12d08b9f0cSSami Tolvanen #include <linux/poison.h> 13d08b9f0cSSami Tolvanen #include <linux/sched.h> 14d08b9f0cSSami Tolvanen #include <linux/sizes.h> 15d08b9f0cSSami Tolvanen 16d08b9f0cSSami Tolvanen #ifdef CONFIG_SHADOW_CALL_STACK 17d08b9f0cSSami Tolvanen 18a2abe7cbSSami Tolvanen #define SCS_ORDER 0 19a2abe7cbSSami Tolvanen #define SCS_SIZE (PAGE_SIZE << SCS_ORDER) 20d08b9f0cSSami Tolvanen #define GFP_SCS (GFP_KERNEL | __GFP_ZERO) 21d08b9f0cSSami Tolvanen 22d08b9f0cSSami Tolvanen /* An illegal pointer value to mark the end of the shadow stack. */ 23d08b9f0cSSami Tolvanen #define SCS_END_MAGIC (0x5f6UL + POISON_POINTER_DELTA) 24d08b9f0cSSami Tolvanen 25d08b9f0cSSami Tolvanen #define task_scs(tsk) (task_thread_info(tsk)->scs_base) 2651189c7aSWill Deacon #define task_scs_sp(tsk) (task_thread_info(tsk)->scs_sp) 27d08b9f0cSSami Tolvanen 28a2abe7cbSSami Tolvanen void *scs_alloc(int node); 29a2abe7cbSSami Tolvanen void scs_free(void *s); 30d08b9f0cSSami Tolvanen void scs_init(void); 31d08b9f0cSSami Tolvanen int scs_prepare(struct task_struct *tsk, int node); 32d08b9f0cSSami Tolvanen void scs_release(struct task_struct *tsk); 33d08b9f0cSSami Tolvanen scs_task_reset(struct task_struct * tsk)34d08b9f0cSSami Tolvanenstatic inline void scs_task_reset(struct task_struct *tsk) 35d08b9f0cSSami Tolvanen { 36d08b9f0cSSami Tolvanen /* 37d08b9f0cSSami Tolvanen * Reset the shadow stack to the base address in case the task 38d08b9f0cSSami Tolvanen * is reused. 39d08b9f0cSSami Tolvanen */ 4051189c7aSWill Deacon task_scs_sp(tsk) = task_scs(tsk); 41d08b9f0cSSami Tolvanen } 42d08b9f0cSSami Tolvanen __scs_magic(void * s)43d08b9f0cSSami Tolvanenstatic inline unsigned long *__scs_magic(void *s) 44d08b9f0cSSami Tolvanen { 45d08b9f0cSSami Tolvanen return (unsigned long *)(s + SCS_SIZE) - 1; 46d08b9f0cSSami Tolvanen } 47d08b9f0cSSami Tolvanen task_scs_end_corrupted(struct task_struct * tsk)4888485be5SWill Deaconstatic inline bool task_scs_end_corrupted(struct task_struct *tsk) 49d08b9f0cSSami Tolvanen { 50d08b9f0cSSami Tolvanen unsigned long *magic = __scs_magic(task_scs(tsk)); 5151189c7aSWill Deacon unsigned long sz = task_scs_sp(tsk) - task_scs(tsk); 52d08b9f0cSSami Tolvanen 5351189c7aSWill Deacon return sz >= SCS_SIZE - 1 || READ_ONCE_NOCHECK(*magic) != SCS_END_MAGIC; 54d08b9f0cSSami Tolvanen } 55d08b9f0cSSami Tolvanen 56*9beccca0SArd Biesheuvel DECLARE_STATIC_KEY_FALSE(dynamic_scs_enabled); 57*9beccca0SArd Biesheuvel scs_is_dynamic(void)58*9beccca0SArd Biesheuvelstatic inline bool scs_is_dynamic(void) 59*9beccca0SArd Biesheuvel { 60*9beccca0SArd Biesheuvel if (!IS_ENABLED(CONFIG_DYNAMIC_SCS)) 61*9beccca0SArd Biesheuvel return false; 62*9beccca0SArd Biesheuvel return static_branch_likely(&dynamic_scs_enabled); 63*9beccca0SArd Biesheuvel } 64*9beccca0SArd Biesheuvel scs_is_enabled(void)65*9beccca0SArd Biesheuvelstatic inline bool scs_is_enabled(void) 66*9beccca0SArd Biesheuvel { 67*9beccca0SArd Biesheuvel if (!IS_ENABLED(CONFIG_DYNAMIC_SCS)) 68*9beccca0SArd Biesheuvel return true; 69*9beccca0SArd Biesheuvel return scs_is_dynamic(); 70*9beccca0SArd Biesheuvel } 71*9beccca0SArd Biesheuvel 72d08b9f0cSSami Tolvanen #else /* CONFIG_SHADOW_CALL_STACK */ 73d08b9f0cSSami Tolvanen scs_alloc(int node)74a2abe7cbSSami Tolvanenstatic inline void *scs_alloc(int node) { return NULL; } scs_free(void * s)75a2abe7cbSSami Tolvanenstatic inline void scs_free(void *s) {} scs_init(void)76d08b9f0cSSami Tolvanenstatic inline void scs_init(void) {} scs_task_reset(struct task_struct * tsk)77d08b9f0cSSami Tolvanenstatic inline void scs_task_reset(struct task_struct *tsk) {} scs_prepare(struct task_struct * tsk,int node)78d08b9f0cSSami Tolvanenstatic inline int scs_prepare(struct task_struct *tsk, int node) { return 0; } scs_release(struct task_struct * tsk)79d08b9f0cSSami Tolvanenstatic inline void scs_release(struct task_struct *tsk) {} task_scs_end_corrupted(struct task_struct * tsk)8088485be5SWill Deaconstatic inline bool task_scs_end_corrupted(struct task_struct *tsk) { return false; } scs_is_enabled(void)81*9beccca0SArd Biesheuvelstatic inline bool scs_is_enabled(void) { return false; } scs_is_dynamic(void)82*9beccca0SArd Biesheuvelstatic inline bool scs_is_dynamic(void) { return false; } 83d08b9f0cSSami Tolvanen 84d08b9f0cSSami Tolvanen #endif /* CONFIG_SHADOW_CALL_STACK */ 85d08b9f0cSSami Tolvanen 86d08b9f0cSSami Tolvanen #endif /* _LINUX_SCS_H */ 87