diff --git a/kernel/Kconfig b/kernel/Kconfig index 4e4796b7ee9..0ccde9ec397 100644 --- a/kernel/Kconfig +++ b/kernel/Kconfig @@ -564,6 +564,26 @@ config EXECUTE_XOR_WRITE If unsure, say Y. +config STACK_POINTER_RANDOM + int + prompt "Initial stack pointer randomization bounds" + depends on !STACK_GROWS_UP + default 0 + help + This option performs a limited form of Address Space Layout + Randomization by offsetting some random value to a thread's + initial stack pointer upon creation. This hinders some types of + security attacks by making the location of any given stack frame + non-deterministic. + + This feature can waste up to the specified size in bytes the stack + region, which is carved out of the total size of the stack region. + A reasonable minimum value would be around 100 bytes if this can + be spared. + + This is currently only implemented for systems whose stack pointers + grow towards lower memory addresses. + endmenu config MAX_DOMAIN_PARTITIONS diff --git a/kernel/thread.c b/kernel/thread.c index 7fb00cafefd..c24eb45669e 100644 --- a/kernel/thread.c +++ b/kernel/thread.c @@ -270,6 +270,19 @@ void _setup_new_thread(struct k_thread *new_thread, void *p1, void *p2, void *p3, int prio, u32_t options) { +#if CONFIG_STACK_POINTER_RANDOM +#if defined(CONFIG_STACK_GROWS_UP) + /* This is so rare not bothering for now */ +#error "Stack pointer randomization not implemented for upward growing stacks" +#endif + /* Don't need to worry about alignment of the size here, _new_thread() + * is required to do it + * + * FIXME: Not the best way to get a random number in a range. + * See #6493 + */ + stack_size -= sys_rand32_get() % CONFIG_STACK_POINTER_RANDOM; +#endif /* CONFIG_STACK_POINTER_RANDOM */ _new_thread(new_thread, stack, stack_size, entry, p1, p2, p3, prio, options); #ifdef CONFIG_USERSPACE diff --git a/tests/kernel/mem_protect/stack_random/CMakeLists.txt b/tests/kernel/mem_protect/stack_random/CMakeLists.txt new file mode 100644 index 00000000000..92a4288bc1d --- /dev/null +++ b/tests/kernel/mem_protect/stack_random/CMakeLists.txt @@ -0,0 +1,5 @@ +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(NONE) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/tests/kernel/mem_protect/stack_random/prj.conf b/tests/kernel/mem_protect/stack_random/prj.conf new file mode 100644 index 00000000000..7087d045f67 --- /dev/null +++ b/tests/kernel/mem_protect/stack_random/prj.conf @@ -0,0 +1,4 @@ +CONFIG_TEST=y +CONFIG_STACK_POINTER_RANDOM=64 +CONFIG_ENTROPY_GENERATOR=y +CONFIG_TEST_RANDOM_GENERATOR=y diff --git a/tests/kernel/mem_protect/stack_random/src/main.c b/tests/kernel/mem_protect/stack_random/src/main.c new file mode 100644 index 00000000000..589ced42704 --- /dev/null +++ b/tests/kernel/mem_protect/stack_random/src/main.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#include +#include + +#define STACKSIZE 2048 +#define THREAD_COUNT 64 +#define VERBOSE 0 + +void *last_sp = (void *)0xFFFFFFFF; +unsigned int changed; + +void alternate_thread(void) +{ + int i; + void *sp_val; + + /* If the stack isn't being randomized then sp_val will never change */ + sp_val = &i; + +#if VERBOSE + printk("stack pointer: %p last: %p\n", sp_val, last_sp); +#endif + + if (last_sp != (void *)0xFFFFFFFF && sp_val != last_sp) { + changed++; + } + last_sp = sp_val; +} + + +K_THREAD_STACK_DEFINE(alt_thread_stack_area, STACKSIZE); +static struct k_thread alt_thread_data; + +void main(void) +{ + int i, ret = TC_FAIL; + + TC_START("Test Stack pointer randomization\n"); + + /* Start thread */ + for (i = 0; i < THREAD_COUNT; i++) { + k_thread_create(&alt_thread_data, alt_thread_stack_area, + STACKSIZE, (k_thread_entry_t)alternate_thread, + NULL, NULL, NULL, K_HIGHEST_THREAD_PRIO, 0, + K_NO_WAIT); + } + + printk("stack pointer changed %d times out of %d tests\n", + changed, THREAD_COUNT); + + if (changed) { + ret = TC_PASS; + } + + TC_END_RESULT(ret); + TC_END_REPORT(ret); +} diff --git a/tests/kernel/mem_protect/stack_random/testcase.yaml b/tests/kernel/mem_protect/stack_random/testcase.yaml new file mode 100644 index 00000000000..05c4912655b --- /dev/null +++ b/tests/kernel/mem_protect/stack_random/testcase.yaml @@ -0,0 +1,4 @@ +tests: + kernel.memory_protection.stack_random: + arch_exclude: posix + tags: core