1 /*
2 * Copyright (c) 2012-2014 Wind River Systems, Inc.
3 * Copyright (c) 2016 Intel Corporation
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8
9 #include <zephyr/kernel.h>
10 #include <zephyr/toolchain.h>
11 #include <zephyr/ztest.h>
12
13
14 #define STACKSIZE (2048 + CONFIG_TEST_EXTRA_STACK_SIZE)
15
16 ZTEST_BMEM static int count;
17 ZTEST_BMEM static int ret = TC_PASS;
18
k_sys_fatal_error_handler(unsigned int reason,const struct arch_esf * esf)19 void k_sys_fatal_error_handler(unsigned int reason, const struct arch_esf *esf)
20 {
21 if (reason != K_ERR_STACK_CHK_FAIL) {
22 printk("wrong error type\n");
23 TC_END_REPORT(TC_FAIL);
24 k_fatal_halt(reason);
25 }
26 }
27
28 void check_input(const char *name, const char *input);
29
30 /**
31 *
32 * print_loop
33 *
34 * This function calls check_input 6 times with the input name and a short
35 * string, which is printed properly by check_input.
36 *
37 * @param name caller identification string
38 *
39 */
40
print_loop(const char * name)41 void print_loop(const char *name)
42 {
43 while (count < 6) {
44 /* A short input string to check_input. It will pass. */
45 check_input(name, "Stack ok");
46 count++;
47 }
48 }
49
50 /**
51 *
52 * check_input
53 *
54 * This function copies the input string to a buffer of 16 characters and
55 * prints the name and buffer as a string. If the input string is longer
56 * than the buffer, an error condition is detected.
57 *
58 * When stack protection feature is enabled (see prj.conf file), the
59 * system error handler is invoked and reports a "Stack Check Fail" error.
60 * When stack protection feature is not enabled, the system crashes with
61 * error like: Trying to execute code outside RAM or ROM.
62 *
63 */
64
check_input(const char * name,const char * input)65 void __noinline check_input(const char *name, const char *input)
66 {
67 /* Stack will overflow when input is more than 16 characters */
68 char buf[16];
69
70 strcpy(buf, input);
71 TC_PRINT("%s: %s\n", name, buf);
72 }
73
74 /**
75 *
76 * This thread passes a long string to check_input function. It terminates due
77 * to stack overflow and reports "Stack Check Fail" when stack protection
78 * feature is enabled. Hence it will not execute the print_loop function
79 * and will not set ret to TC_FAIL.
80 *
81 */
alternate_thread(void * p1,void * p2,void * p3)82 void alternate_thread(void *p1, void *p2, void *p3)
83 {
84 ARG_UNUSED(p1);
85 ARG_UNUSED(p2);
86 ARG_UNUSED(p3);
87
88 TC_PRINT("Starts %s\n", __func__);
89 check_input(__func__,
90 "Input string is too long and stack overflowed!\n");
91 /*
92 * Expect this thread to terminate due to stack check fail and will not
93 * execute pass here.
94 */
95 print_loop(__func__);
96
97 ret = TC_FAIL;
98 }
99
100
101
102 K_THREAD_STACK_DEFINE(alt_thread_stack_area, STACKSIZE);
103 static struct k_thread alt_thread_data;
104
105 /**
106 * @brief test Stack Protector feature using canary
107 *
108 * @details This is the test program to test stack protection using canary.
109 * The main thread starts a second thread, which generates a stack check
110 * failure.
111 * By design, the second thread will not complete its execution and
112 * will not set ret to TC_FAIL.
113 * This is the entry point to the test stack protection feature.
114 * It starts the thread that tests stack protection, then prints out
115 * a few messages before terminating.
116 *
117 * @ingroup kernel_memprotect_tests
118 */
ZTEST_USER(stackprot,test_stackprot)119 ZTEST_USER(stackprot, test_stackprot)
120 {
121 zassert_true(ret == TC_PASS);
122 print_loop(__func__);
123 }
124
125 /**
126 * @brief Test optional mechanism to detect stack overflow
127 *
128 * @details Test that the system provides an optional mechanism to detect
129 * when supervisor threads overflow stack memory buffer.
130 *
131 * @ingroup kernel_memprotect_tests
132 */
ZTEST(stackprot,test_create_alt_thread)133 ZTEST(stackprot, test_create_alt_thread)
134 {
135 /* Start thread */
136 k_thread_create(&alt_thread_data, alt_thread_stack_area, STACKSIZE,
137 alternate_thread, NULL, NULL, NULL,
138 K_PRIO_COOP(1), K_USER, K_NO_WAIT);
139
140 /* Note that this sleep is required on SMP platforms where
141 * that thread will execute asynchronously!
142 */
143 k_sleep(K_MSEC(100));
144 }
145
146 #ifdef CONFIG_STACK_CANARIES_TLS
147 extern Z_THREAD_LOCAL volatile uintptr_t __stack_chk_guard;
148 #else
149 extern volatile uintptr_t __stack_chk_guard;
150 #endif
151
152 /**
153 * This thread checks its canary value against its parent canary.
154 * If CONFIG_STACK_CANARIES_TLS is enabled, it is expected that the
155 * canaries have different values, otherwise there is only one global
156 * canary and the value should be the same.
157 */
alternate_thread_canary(void * arg1,void * arg2,void * arg3)158 void alternate_thread_canary(void *arg1, void *arg2, void *arg3)
159 {
160 ARG_UNUSED(arg2);
161 ARG_UNUSED(arg3);
162
163 TC_PRINT("Starts %s\n", __func__);
164
165 #ifdef CONFIG_STACK_CANARIES_TLS
166 zassert_false(__stack_chk_guard == (uintptr_t)arg1);
167 #else
168 zassert_true(__stack_chk_guard == (uintptr_t)arg1);
169 #endif
170 }
171
172 /**
173 * @brief Test stack canaries behavior
174 *
175 * @details Test that canaries value are different between threads when
176 * CONFIG_STACK_CANARIES_TLS is enabled.
177 *
178 * @ingroup kernel_memprotect_tests
179 */
ZTEST(stackprot,test_canary_value)180 ZTEST(stackprot, test_canary_value)
181 {
182 /* Start thread */
183 k_thread_create(&alt_thread_data, alt_thread_stack_area, STACKSIZE,
184 alternate_thread_canary,
185 (void *)__stack_chk_guard, NULL, NULL,
186 K_PRIO_COOP(1), K_USER, K_NO_WAIT);
187
188 /* Note that this sleep is required on SMP platforms where
189 * that thread will execute asynchronously!
190 */
191 k_sleep(K_MSEC(100));
192 }
193
194 ZTEST_SUITE(stackprot, NULL, NULL, NULL, NULL, NULL);
195