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