xref: /linux/include/linux/scs.h (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
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 Tolvanen static 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 Tolvanen static 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 Deacon static 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 Biesheuvel static 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 Biesheuvel static 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 Tolvanen static inline void *scs_alloc(int node) { return NULL; }
scs_free(void * s)75a2abe7cbSSami Tolvanen static inline void scs_free(void *s) {}
scs_init(void)76d08b9f0cSSami Tolvanen static inline void scs_init(void) {}
scs_task_reset(struct task_struct * tsk)77d08b9f0cSSami Tolvanen static inline void scs_task_reset(struct task_struct *tsk) {}
scs_prepare(struct task_struct * tsk,int node)78d08b9f0cSSami Tolvanen static inline int scs_prepare(struct task_struct *tsk, int node) { return 0; }
scs_release(struct task_struct * tsk)79d08b9f0cSSami Tolvanen static inline void scs_release(struct task_struct *tsk) {}
task_scs_end_corrupted(struct task_struct * tsk)8088485be5SWill Deacon static inline bool task_scs_end_corrupted(struct task_struct *tsk) { return false; }
scs_is_enabled(void)81*9beccca0SArd Biesheuvel static inline bool scs_is_enabled(void) { return false; }
scs_is_dynamic(void)82*9beccca0SArd Biesheuvel static 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