1 /*
2 * Parts derived from tests/kernel/fatal/src/main.c, which has the
3 * following copyright and license:
4 *
5 * Copyright (c) 2017 Intel Corporation
6 *
7 * SPDX-License-Identifier: Apache-2.0
8 */
9
10 #include <zephyr/kernel.h>
11 #include <zephyr/ztest.h>
12 #include <zephyr/kernel_structs.h>
13 #include <zephyr/sys/barrier.h>
14 #include <zephyr/toolchain.h>
15 #include <string.h>
16 #include <stdlib.h>
17
18 #include "targets.h"
19
20 /* 32-bit IA32 page tables have no mechanism to restrict execution */
21 #if defined(CONFIG_X86) && !defined(CONFIG_X86_64) && !defined(CONFIG_X86_PAE)
22 #define SKIP_EXECUTE_TESTS
23 #endif
24
25 /* RISC-V have no mechanism to restrict execution */
26 #if defined(CONFIG_RISCV)
27 #define SKIP_EXECUTE_TESTS
28 #endif
29
30 #define INFO(fmt, ...) printk(fmt, ##__VA_ARGS__)
31
k_sys_fatal_error_handler(unsigned int reason,const struct arch_esf * pEsf)32 void k_sys_fatal_error_handler(unsigned int reason, const struct arch_esf *pEsf)
33 {
34 INFO("Caught system error -- reason %d\n", reason);
35 ztest_test_pass();
36 }
37
38 #ifdef CONFIG_COMPILER_ISA_THUMB2
39 /* Must clear LSB of function address to access as data. */
40 #define FUNC_TO_PTR(x) (void *)((uintptr_t)(x) & ~0x1)
41 /* Must set LSB of function address to call in Thumb mode. */
42 #define PTR_TO_FUNC(x) (int (*)(int))((uintptr_t)(x) | 0x1)
43 /* Flush preceding data writes and instruction fetches. */
44 #define DO_BARRIERS() do { barrier_dsync_fence_full(); \
45 barrier_isync_fence_full(); \
46 } while (0)
47 #else
48 #define FUNC_TO_PTR(x) (void *)(x)
49 #define PTR_TO_FUNC(x) (int (*)(int))(x)
50 #define DO_BARRIERS() do { } while (0)
51 #endif
52
add_one(int i)53 static int __noinline add_one(int i)
54 {
55 return (i + 1);
56 }
57
58 #ifndef SKIP_EXECUTE_TESTS
execute_from_buffer(uint8_t * dst)59 static void execute_from_buffer(uint8_t *dst)
60 {
61 void *src = FUNC_TO_PTR(add_one);
62 int (*func)(int i) = PTR_TO_FUNC(dst);
63 int i = 1;
64
65 /* Copy add_one() code to destination buffer. */
66 memcpy(dst, src, BUF_SIZE);
67 DO_BARRIERS();
68
69 /*
70 * Try executing from buffer we just filled.
71 * Optimally, this triggers a fault.
72 * If not, we check to see if the function
73 * returned the expected result as confirmation
74 * that we truly executed the code we wrote.
75 */
76 INFO("trying to call code written to %p\n", func);
77 i = func(i);
78 INFO("returned from code at %p\n", func);
79 if (i == 2) {
80 INFO("Execute from target buffer succeeded!\n");
81 } else {
82 INFO("Did not get expected return value!\n");
83 }
84 }
85 #endif /* SKIP_EXECUTE_TESTS */
86
87 /**
88 * @brief Test write to read only section
89 *
90 * @ingroup kernel_memprotect_tests
91 */
ZTEST(protection,test_write_ro)92 ZTEST(protection, test_write_ro)
93 {
94 volatile uint32_t *ptr = (volatile uint32_t *)&rodata_var;
95
96 /*
97 * Try writing to rodata. Optimally, this triggers a fault.
98 * If not, we check to see if the rodata value actually changed.
99 */
100 INFO("trying to write to rodata at %p\n", ptr);
101 *ptr = ~RODATA_VALUE;
102
103 DO_BARRIERS();
104
105 if (*ptr == RODATA_VALUE) {
106 INFO("rodata value still the same\n");
107 } else if (*ptr == ~RODATA_VALUE) {
108 INFO("rodata modified!\n");
109 } else {
110 INFO("something went wrong!\n");
111 }
112
113 zassert_unreachable("Write to rodata did not fault");
114 }
115
116 /**
117 * @brief Test to execute on text section
118 *
119 * @ingroup kernel_memprotect_tests
120 */
ZTEST(protection,test_write_text)121 ZTEST(protection, test_write_text)
122 {
123 void *src = FUNC_TO_PTR(add_one);
124 void *dst = FUNC_TO_PTR(overwrite_target);
125 int i = 1;
126
127 /*
128 * Try writing to a function in the text section.
129 * Optimally, this triggers a fault.
130 * If not, we try calling the function after overwriting
131 * to see if it returns the expected result as
132 * confirmation that we truly executed the code we wrote.
133 */
134 INFO("trying to write to text at %p\n", dst);
135 memcpy(dst, src, BUF_SIZE);
136 DO_BARRIERS();
137 i = overwrite_target(i);
138 if (i == 2) {
139 INFO("Overwrite of text succeeded!\n");
140 } else {
141 INFO("Did not get expected return value!\n");
142 }
143
144 zassert_unreachable("Write to text did not fault");
145 }
146
147 /**
148 * @brief Test execution from data section
149 *
150 * @ingroup kernel_memprotect_tests
151 */
ZTEST(protection,test_exec_data)152 ZTEST(protection, test_exec_data)
153 {
154 #ifdef SKIP_EXECUTE_TESTS
155 ztest_test_skip();
156 #else
157 execute_from_buffer(data_buf);
158 zassert_unreachable("Execute from data did not fault");
159 #endif
160 }
161
162 /**
163 * @brief Test execution from stack section
164 *
165 * @ingroup kernel_memprotect_tests
166 */
ZTEST(protection,test_exec_stack)167 ZTEST(protection, test_exec_stack)
168 {
169 #ifdef SKIP_EXECUTE_TESTS
170 ztest_test_skip();
171 #else
172 uint8_t stack_buf[BUF_SIZE] __aligned(sizeof(int));
173
174 execute_from_buffer(stack_buf);
175 zassert_unreachable("Execute from stack did not fault");
176 #endif
177 }
178
179 /**
180 * @brief Test execution from heap
181 *
182 * @ingroup kernel_memprotect_tests
183 */
ZTEST(protection,test_exec_heap)184 ZTEST(protection, test_exec_heap)
185 {
186 #if (CONFIG_HEAP_MEM_POOL_SIZE > 0) && !defined(SKIP_EXECUTE_TESTS)
187 uint8_t *heap_buf = k_malloc(BUF_SIZE);
188
189 execute_from_buffer(heap_buf);
190 k_free(heap_buf);
191 zassert_unreachable("Execute from heap did not fault");
192 #else
193 ztest_test_skip();
194 #endif
195 }
196
197 ZTEST_SUITE(protection, NULL, NULL, NULL, NULL, NULL);
198