Skip to content

Commit d1b68c7

Browse files
kernel: Automatically set up HW shadow stack for thread stacks
This patch modifies thread stack macros (such as K_KERNEL_STACK_DECLARE or K_KERNEL_STACK_ARRAY_DECLARE) to also create a HW shadow stack (when CONFIG_HW_SHADOW_STACK=y), as well as define a pairing between the thread stack (or thread stack array) and the shadow stack (or shadow stack array). This pairing, which currently is simply an array of pairs (stack, shadow_stack) is searched during thread setup to find the corresponding shadow stack and attach it to the thread. If linear search on this array proves to be a performance issue, the actual structure can be revisited. To define the size of the shadow stack for a given stack, the stack size is used. A new Kconfig, CONFIG_HW_SHADOW_STACK_PERCENTAGE_SIZE is used to define how big the shadow stack is compared to the stack. Note that this size is in *addition* to the stack size. To avoid some shadow stacks becoming too small, CONFIG_HW_SHADOW_STACK_MIN_SIZE is used to define a minimum size. Note that after this size is defined, platform restrictions on the size of the shadow stack are still applied. Signed-off-by: Ederson de Souza <ederson.desouza@intel.com>
1 parent 399b28a commit d1b68c7

File tree

4 files changed

+190
-14
lines changed

4 files changed

+190
-14
lines changed

include/zephyr/kernel/thread_stack.h

Lines changed: 92 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,79 @@ static inline char *z_stack_ptr_align(char *ptr)
112112
* @{
113113
*/
114114

115+
#ifdef CONFIG_HW_SHADOW_STACK
116+
#define k_thread_hw_shadow_stack_t arch_thread_hw_shadow_stack_t
117+
118+
#define K_THREAD_HW_SHADOW_STACK_SIZE(size_) \
119+
MAX(ROUND_UP((CONFIG_HW_SHADOW_STACK_PERCENTAGE_SIZE * (size_) / 100), \
120+
CONFIG_X86_CET_SHADOW_STACK_ALIGNMENT), \
121+
CONFIG_HW_SHADOW_STACK_MIN_SIZE)
122+
123+
#define K_KERNEL_HW_SHADOW_STACK_DECLARE(sym, size) \
124+
ARCH_THREAD_HW_SHADOW_STACK_DECLARE(__ ## sym ## _shstk, size)
125+
126+
#define K_KERNEL_HW_SHADOW_STACK_ARRAY_DECLARE(sym, nmemb, size) \
127+
ARCH_THREAD_HW_SHADOW_STACK_ARRAY_DECLARE(__ ## sym ## _shstk_arr, \
128+
nmemb, size)
129+
130+
struct _stack_to_hw_shadow_stack {
131+
k_thread_stack_t *stack;
132+
k_thread_hw_shadow_stack_t *shstk_addr;
133+
size_t size;
134+
};
135+
136+
#define K_THREAD_HW_SHADOW_STACK_DEFINE(sym, size_) \
137+
ARCH_THREAD_HW_SHADOW_STACK_DEFINE(__ ## sym ## _shstk, size_); \
138+
static const STRUCT_SECTION_ITERABLE(_stack_to_hw_shadow_stack, \
139+
sym ## _stack_to_shstk_attach) = { \
140+
.stack = sym, \
141+
.shstk_addr = __ ## sym ## _shstk, \
142+
.size = size_, \
143+
}
144+
145+
struct _stack_to_hw_shadow_stack_arr {
146+
uintptr_t stack_addr;
147+
uintptr_t shstk_addr;
148+
size_t stack_size;
149+
size_t shstk_size;
150+
size_t nmemb;
151+
};
152+
153+
#define K_THREAD_HW_SHADOW_STACK_ARRAY_DEFINE(sym, nmemb_, size_) \
154+
ARCH_THREAD_HW_SHADOW_STACK_ARRAY_DEFINE(__ ## sym ## _shstk_arr, nmemb_, \
155+
K_THREAD_HW_SHADOW_STACK_SIZE(size_)); \
156+
static const STRUCT_SECTION_ITERABLE(_stack_to_hw_shadow_stack_arr, \
157+
sym ## _stack_to_shstk_attach) = { \
158+
.stack_addr = (uintptr_t)sym, \
159+
.stack_size = K_KERNEL_STACK_LEN(size_), \
160+
.nmemb = nmemb_, \
161+
.shstk_addr = (uintptr_t)__ ## sym ## _shstk_arr, \
162+
.shstk_size = K_THREAD_HW_SHADOW_STACK_SIZE(size_), \
163+
}
164+
165+
#define k_thread_hw_shadow_stack_attach arch_thread_hw_shadow_stack_attach
166+
167+
struct _thread_hw_shadow_stack_static {
168+
struct k_thread *thread;
169+
k_thread_hw_shadow_stack_t *shstk_addr;
170+
size_t size;
171+
};
172+
173+
#define K_THREAD_HW_SHADOW_STACK_ATTACH(thread_, shstk_addr_, size_) \
174+
static const STRUCT_SECTION_ITERABLE(_thread_hw_shadow_stack_static, \
175+
thread ## _shstk_attach_static) = { \
176+
.thread = thread_, \
177+
.shstk_addr = shstk_addr_, \
178+
.size = size_, \
179+
}
180+
181+
#else
182+
#define K_KERNEL_HW_SHADOW_STACK_DECLARE(sym, size)
183+
#define K_KERNEL_HW_SHADOW_STACK_ARRAY_DECLARE(sym, nmemb, size)
184+
#define K_THREAD_HW_SHADOW_STACK_DEFINE(sym, size)
185+
#define K_THREAD_HW_SHADOW_STACK_ARRAY_DEFINE(sym, nmemb, size_)
186+
#endif
187+
115188
/**
116189
* @brief Declare a reference to a thread stack
117190
*
@@ -122,6 +195,7 @@ static inline char *z_stack_ptr_align(char *ptr)
122195
* @param size Size of the stack memory region
123196
*/
124197
#define K_KERNEL_STACK_DECLARE(sym, size) \
198+
K_KERNEL_HW_SHADOW_STACK_DECLARE(sym, K_THREAD_HW_SHADOW_STACK_SIZE(size)); \
125199
extern struct z_thread_stack_element \
126200
sym[K_KERNEL_STACK_LEN(size)]
127201

@@ -136,6 +210,7 @@ static inline char *z_stack_ptr_align(char *ptr)
136210
* @param size Size of the stack memory region
137211
*/
138212
#define K_KERNEL_STACK_ARRAY_DECLARE(sym, nmemb, size) \
213+
K_KERNEL_HW_SHADOW_STACK_ARRAY_DECLARE(sym, nmemb, K_THREAD_HW_SHADOW_STACK_SIZE(size)); \
139214
extern struct z_thread_stack_element \
140215
sym[nmemb][K_KERNEL_STACK_LEN(size)]
141216

@@ -150,6 +225,7 @@ static inline char *z_stack_ptr_align(char *ptr)
150225
* @param size Size of the stack memory region
151226
*/
152227
#define K_KERNEL_PINNED_STACK_ARRAY_DECLARE(sym, nmemb, size) \
228+
K_KERNEL_HW_SHADOW_STACK_ARRAY_DECLARE(sym, nmemb, K_THREAD_HW_SHADOW_STACK_SIZE(size)); \
153229
extern struct z_thread_stack_element \
154230
sym[nmemb][K_KERNEL_STACK_LEN(size)]
155231

@@ -212,7 +288,9 @@ static inline char *z_stack_ptr_align(char *ptr)
212288
* @param size Size of the stack memory region
213289
*/
214290
#define K_KERNEL_STACK_DEFINE(sym, size) \
215-
Z_KERNEL_STACK_DEFINE_IN(sym, size, __kstackmem)
291+
Z_KERNEL_STACK_DEFINE_IN(sym, size, __kstackmem); \
292+
K_THREAD_HW_SHADOW_STACK_DEFINE(sym, \
293+
K_THREAD_HW_SHADOW_STACK_SIZE(size))
216294

217295
/**
218296
* @brief Define a toplevel kernel stack memory region in pinned section
@@ -228,10 +306,14 @@ static inline char *z_stack_ptr_align(char *ptr)
228306
*/
229307
#if defined(CONFIG_LINKER_USE_PINNED_SECTION)
230308
#define K_KERNEL_PINNED_STACK_DEFINE(sym, size) \
231-
Z_KERNEL_STACK_DEFINE_IN(sym, size, __pinned_noinit)
309+
Z_KERNEL_STACK_DEFINE_IN(sym, size, __pinned_noinit); \
310+
K_THREAD_HW_SHADOW_STACK_DEFINE(sym, \
311+
K_THREAD_HW_SHADOW_STACK_SIZE(size))
232312
#else
233313
#define K_KERNEL_PINNED_STACK_DEFINE(sym, size) \
234-
Z_KERNEL_STACK_DEFINE_IN(sym, size, __kstackmem)
314+
Z_KERNEL_STACK_DEFINE_IN(sym, size, __kstackmem); \
315+
K_THREAD_HW_SHADOW_STACK_DEFINE(sym, \
316+
K_THREAD_HW_SHADOW_STACK_SIZE(size))
235317
#endif /* CONFIG_LINKER_USE_PINNED_SECTION */
236318

237319
/**
@@ -244,7 +326,9 @@ static inline char *z_stack_ptr_align(char *ptr)
244326
* @param size Size of the stack memory region
245327
*/
246328
#define K_KERNEL_STACK_ARRAY_DEFINE(sym, nmemb, size) \
247-
Z_KERNEL_STACK_ARRAY_DEFINE_IN(sym, nmemb, size, __kstackmem)
329+
Z_KERNEL_STACK_ARRAY_DEFINE_IN(sym, nmemb, size, __kstackmem); \
330+
K_THREAD_HW_SHADOW_STACK_ARRAY_DEFINE(sym, nmemb, size)
331+
248332

249333
/**
250334
* @brief Define a toplevel array of kernel stack memory regions in pinned section
@@ -261,10 +345,12 @@ static inline char *z_stack_ptr_align(char *ptr)
261345
*/
262346
#if defined(CONFIG_LINKER_USE_PINNED_SECTION)
263347
#define K_KERNEL_PINNED_STACK_ARRAY_DEFINE(sym, nmemb, size) \
264-
Z_KERNEL_STACK_ARRAY_DEFINE_IN(sym, nmemb, size, __pinned_noinit)
348+
Z_KERNEL_STACK_ARRAY_DEFINE_IN(sym, nmemb, size, __pinned_noinit); \
349+
K_THREAD_HW_SHADOW_STACK_ARRAY_DEFINE(sym, nmemb, size)
265350
#else
266351
#define K_KERNEL_PINNED_STACK_ARRAY_DEFINE(sym, nmemb, size) \
267-
Z_KERNEL_STACK_ARRAY_DEFINE_IN(sym, nmemb, size, __kstackmem)
352+
Z_KERNEL_STACK_ARRAY_DEFINE_IN(sym, nmemb, size, __kstackmem); \
353+
K_THREAD_HW_SHADOW_STACK_ARRAY_DEFINE(sym, nmemb, size)
268354
#endif /* CONFIG_LINKER_USE_PINNED_SECTION */
269355

270356
/**
@@ -283,14 +369,6 @@ static inline char *z_stack_ptr_align(char *ptr)
283369

284370
/** @} */
285371

286-
#ifdef CONFIG_HW_SHADOW_STACK
287-
#define K_THREAD_HW_SHADOW_STACK_DEFINE ARCH_THREAD_HW_SHADOW_STACK_DEFINE
288-
289-
#define k_thread_hw_shadow_stack_t arch_thread_hw_shadow_stack_t
290-
291-
#define k_thread_hw_shadow_stack_attach arch_thread_hw_shadow_stack_attach
292-
#endif
293-
294372
static inline char *K_KERNEL_STACK_BUFFER(k_thread_stack_t *sym)
295373
{
296374
return (char *)sym + K_KERNEL_STACK_RESERVED;

include/zephyr/linker/common-rom/common-rom-kernel-devices.ld

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,9 @@
102102
} GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
103103
#endif /* !CONFIG_DEVICE_DEPS_DYNAMIC */
104104

105+
#ifdef CONFIG_HW_SHADOW_STACK
106+
ITERABLE_SECTION_ROM(_stack_to_hw_shadow_stack, Z_LINK_ITERABLE_SUBALIGN)
107+
ITERABLE_SECTION_ROM(_stack_to_hw_shadow_stack_arr, Z_LINK_ITERABLE_SUBALIGN)
108+
#endif
109+
105110
#include <device-api-sections.ld>

kernel/Kconfig

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,31 @@ config HW_SHADOW_STACK
10451045
stack. Indeed, threads need to manually attach a shadow stack to
10461046
use this feature. See the documentation for more information.
10471047

1048+
config HW_SHADOW_STACK_PERCENTAGE_SIZE
1049+
int "Percentage of the stack size used to define shadow stack size"
1050+
depends on HW_SHADOW_STACK
1051+
default 30
1052+
range 0 100
1053+
help
1054+
This option specifies the percentage of the stack to be used for
1055+
shadow stack. The value is a percentage of the total stack size.
1056+
The default value is 30%, which means that the shadow stack size
1057+
will be 30% of the stack size, in *addition* to the stack size.
1058+
used for shadow stack. Note that this size is constrained by the
1059+
HW_SHADOW_STACK_MIN_SIZE config option.
1060+
1061+
config HW_SHADOW_STACK_MIN_SIZE
1062+
int "Minimum size of the hardware shadow stack"
1063+
depends on HW_SHADOW_STACK
1064+
default 256
1065+
help
1066+
This option specifies the minimum size of the hardware shadow stack,
1067+
in bytes, so that threads that use minimal stack size can still
1068+
have a sensible shadow stack size. The default value is 256 bytes,
1069+
which means that the shadow stack size will be at least 256 bytes,
1070+
even if the percentage defined via HW_SHADOW_STACK_PERCENTAGE_SIZE
1071+
would define a smaller size.
1072+
10481073
config HW_SHADOW_STACK_ALLOW_REUSE
10491074
bool "Allow reuse of the shadow stack"
10501075
depends on HW_SHADOW_STACK

kernel/thread.c

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,70 @@ static char *setup_thread_stack(struct k_thread *new_thread,
501501
return stack_ptr;
502502
}
503503

504+
#ifdef CONFIG_HW_SHADOW_STACK
505+
static void setup_shadow_stack(struct k_thread *new_thread,
506+
k_thread_stack_t *stack)
507+
{
508+
int ret = -ENOENT;
509+
510+
STRUCT_SECTION_FOREACH(_stack_to_hw_shadow_stack, stk_to_hw_shstk) {
511+
if (stk_to_hw_shstk->stack == stack) {
512+
ret = k_thread_hw_shadow_stack_attach(new_thread,
513+
stk_to_hw_shstk->shstk_addr,
514+
stk_to_hw_shstk->size);
515+
if (ret != 0) {
516+
LOG_ERR("Could not set thread %p shadow stack %p, got error %d",
517+
new_thread, stk_to_hw_shstk->shstk_addr, ret);
518+
k_panic();
519+
}
520+
break;
521+
}
522+
}
523+
524+
/* Check if the stack isn't in a stack array, and use the corresponding
525+
* shadow stack.
526+
*/
527+
if (ret != -ENOENT) {
528+
return;
529+
}
530+
531+
STRUCT_SECTION_FOREACH(_stack_to_hw_shadow_stack_arr, stk_to_hw_shstk) {
532+
if ((uintptr_t)stack >= stk_to_hw_shstk->stack_addr &&
533+
(uintptr_t)stack < stk_to_hw_shstk->stack_addr +
534+
stk_to_hw_shstk->stack_size * stk_to_hw_shstk->nmemb) {
535+
/* Now we have to guess which index of the stack array is being used */
536+
uintptr_t stack_offset = (uintptr_t)stack - stk_to_hw_shstk->stack_addr;
537+
uintptr_t stack_index = stack_offset / stk_to_hw_shstk->stack_size;
538+
uintptr_t addr;
539+
540+
if (stack_index >= stk_to_hw_shstk->nmemb) {
541+
LOG_ERR("Could not find shadow stack for thread %p, stack %p",
542+
new_thread, stack);
543+
k_panic();
544+
}
545+
546+
addr = stk_to_hw_shstk->shstk_addr +
547+
stk_to_hw_shstk->shstk_size * stack_index;
548+
ret = k_thread_hw_shadow_stack_attach(new_thread,
549+
(arch_thread_hw_shadow_stack_t *)addr,
550+
stk_to_hw_shstk->shstk_size);
551+
if (ret != 0) {
552+
LOG_ERR("Could not set thread %p shadow stack 0x%lx, got error %d",
553+
new_thread, stk_to_hw_shstk->shstk_addr, ret);
554+
k_panic();
555+
}
556+
break;
557+
}
558+
}
559+
560+
if (ret == -ENOENT) {
561+
LOG_ERR("Could not find shadow stack for thread %p, stack %p",
562+
new_thread, stack);
563+
k_panic();
564+
}
565+
}
566+
#endif
567+
504568
/*
505569
* The provided stack_size value is presumed to be either the result of
506570
* K_THREAD_STACK_SIZEOF(stack), or the size value passed to the instance
@@ -547,6 +611,10 @@ char *z_setup_new_thread(struct k_thread *new_thread,
547611
z_init_thread_base(&new_thread->base, prio, _THREAD_SLEEPING, options);
548612
stack_ptr = setup_thread_stack(new_thread, stack, stack_size);
549613

614+
#ifdef CONFIG_HW_SHADOW_STACK
615+
setup_shadow_stack(new_thread, stack);
616+
#endif
617+
550618
#ifdef CONFIG_KERNEL_COHERENCE
551619
/* Check that the thread object is safe, but that the stack is
552620
* still cached!

0 commit comments

Comments
 (0)