1 /*
2  * Copyright (c) 2023 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/ztest.h>
9 #include <zephyr/kernel/thread_stack.h>
10 
11 #ifdef CONFIG_THREAD_STACK_MEM_MAPPED
12 
13 #define STACK_SIZE (CONFIG_MMU_PAGE_SIZE + CONFIG_TEST_EXTRA_STACK_SIZE)
14 
15 K_THREAD_STACK_DEFINE(mapped_thread_stack_area, STACK_SIZE);
16 static struct k_thread mapped_thread_data;
17 
18 ZTEST_BMEM static char *mapped_stack_addr;
19 ZTEST_BMEM static size_t mapped_stack_sz;
20 
21 /**
22  * @brief To cause fault in guard pages.
23  *
24  * @param p1 0 if testing rear guard page, 1 if testing front guard page.
25  * @param p2 Unused.
26  * @param p3 Unused.
27  */
mapped_thread(void * p1,void * p2,void * p3)28 void mapped_thread(void *p1, void *p2, void *p3)
29 {
30 	bool is_front = p1 == NULL ? false : true;
31 	volatile char *ptr;
32 
33 	ARG_UNUSED(p2);
34 	ARG_UNUSED(p3);
35 
36 	TC_PRINT("Starts %s\n", __func__);
37 	TC_PRINT("Mapped stack %p size %zu\n", mapped_stack_addr, mapped_stack_sz);
38 
39 	ptr = mapped_stack_addr;
40 
41 	/* Figure out where to cause the stack fault. */
42 	if (is_front) {
43 		/* Middle of front guard page. */
44 		ptr -= CONFIG_MMU_PAGE_SIZE / 2;
45 	} else {
46 		/* Middle of rear guard page. */
47 		ptr += mapped_stack_sz;
48 		ptr += CONFIG_MMU_PAGE_SIZE / 2;
49 	}
50 
51 	TC_PRINT("Trying to cause stack fault at %p\n", ptr);
52 
53 	*ptr = 0;
54 
55 	TC_PRINT("Should have fault on guard page but not!\n");
56 	ztest_test_fail();
57 }
58 
59 /**
60  * @brief To create thread to fault on guard pages.
61  *
62  * @param is_front True if testing front guard page, false if testing rear guard page.
63  * @param is_user True if creating a user thread, false if creating a kernel thread.
64  */
create_thread(bool is_front,bool is_user)65 void create_thread(bool is_front, bool is_user)
66 {
67 	/* Start thread */
68 	k_thread_create(&mapped_thread_data, mapped_thread_stack_area, STACK_SIZE,
69 			mapped_thread,
70 			is_front ? (void *)1 : (void *)0,
71 			NULL, NULL,
72 			K_PRIO_COOP(1), is_user ? K_USER : 0, K_FOREVER);
73 
74 	zassert_true(mapped_thread_data.stack_info.mapped.addr != NULL);
75 
76 	/* Grab the mapped stack object address and size so we can calculate
77 	 * where to cause a stack fault.
78 	 */
79 	mapped_stack_addr = (void *)mapped_thread_data.stack_info.mapped.addr;
80 	mapped_stack_sz = mapped_thread_data.stack_info.mapped.sz;
81 
82 	k_thread_start(&mapped_thread_data);
83 
84 	/* Note that this sleep is required on SMP platforms where
85 	 * that thread will execute asynchronously!
86 	 */
87 	k_sleep(K_MSEC(100));
88 
89 	k_thread_join(&mapped_thread_data, K_FOREVER);
90 }
91 
92 #endif /* CONFIG_THREAD_STACK_MEM_MAPPED */
93 
94 /**
95  * @brief Test faulting on front guard page
96  *
97  * @ingroup kernel_memprotect_tests
98  */
ZTEST(stackprot_mapped_stack,test_guard_page_front)99 ZTEST(stackprot_mapped_stack, test_guard_page_front)
100 {
101 #ifdef CONFIG_THREAD_STACK_MEM_MAPPED
102 	create_thread(true, false);
103 #else
104 	ztest_test_skip();
105 #endif
106 }
107 
108 /**
109  * @brief Test faulting on rear guard page
110  *
111  * @ingroup kernel_memprotect_tests
112  */
ZTEST(stackprot_mapped_stack,test_guard_page_rear)113 ZTEST(stackprot_mapped_stack, test_guard_page_rear)
114 {
115 #ifdef CONFIG_THREAD_STACK_MEM_MAPPED
116 	create_thread(false, false);
117 #else
118 	ztest_test_skip();
119 #endif
120 }
121 
122 /**
123  * @brief Test faulting on front guard page in user mode
124  *
125  * @ingroup kernel_memprotect_tests
126  */
ZTEST(stackprot_mapped_stack,test_guard_page_front_user)127 ZTEST(stackprot_mapped_stack, test_guard_page_front_user)
128 {
129 #ifdef CONFIG_THREAD_STACK_MEM_MAPPED
130 	create_thread(true, true);
131 #else
132 	ztest_test_skip();
133 #endif
134 }
135 
136 /**
137  * @brief Test faulting on rear guard page in user mode
138  *
139  * @ingroup kernel_memprotect_tests
140  */
ZTEST(stackprot_mapped_stack,test_guard_page_rear_user)141 ZTEST(stackprot_mapped_stack, test_guard_page_rear_user)
142 {
143 #ifdef CONFIG_THREAD_STACK_MEM_MAPPED
144 	create_thread(false, true);
145 #else
146 	ztest_test_skip();
147 #endif
148 }
149 
150 
151 ZTEST_SUITE(stackprot_mapped_stack, NULL, NULL, NULL, NULL, NULL);
152