1 /*
2  * Copyright (c) 2018 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/ztest.h>
8 #include <zephyr/irq_offload.h>
9 #include <zephyr/debug/stack.h>
10 
11 #define STACKSIZE (256 + CONFIG_TEST_EXTRA_STACK_SIZE)
12 
13 static K_THREAD_STACK_DEFINE(dyn_thread_stack, STACKSIZE);
14 static K_SEM_DEFINE(start_sem, 0, 1);
15 static K_SEM_DEFINE(end_sem, 0, 1);
16 static ZTEST_BMEM struct k_thread *dyn_thread;
17 static struct k_thread *dynamic_threads[CONFIG_MAX_THREAD_BYTES * 8];
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_KERNEL_OOPS) {
22 		printk("wrong error reason\n");
23 		TC_END_REPORT(TC_FAIL);
24 		k_fatal_halt(reason);
25 	}
26 	if (k_current_get() != dyn_thread) {
27 		printk("wrong thread crashed\n");
28 		TC_END_REPORT(TC_FAIL);
29 		k_fatal_halt(reason);
30 	}
31 }
32 
dyn_thread_entry(void * p1,void * p2,void * p3)33 static void dyn_thread_entry(void *p1, void *p2, void *p3)
34 {
35 	k_sem_take(&start_sem, K_FOREVER);
36 
37 	k_sem_give(&end_sem);
38 }
39 
prep(void)40 static void prep(void)
41 {
42 	k_thread_access_grant(k_current_get(), dyn_thread_stack,
43 			      &start_sem, &end_sem);
44 }
45 
create_dynamic_thread(void)46 static void create_dynamic_thread(void)
47 {
48 	k_tid_t tid;
49 
50 	dyn_thread = k_object_alloc(K_OBJ_THREAD);
51 
52 	zassert_not_null(dyn_thread, "Cannot allocate thread k_object!");
53 
54 	tid = k_thread_create(dyn_thread, dyn_thread_stack, STACKSIZE,
55 			      dyn_thread_entry, NULL, NULL, NULL,
56 			      K_PRIO_PREEMPT(0), K_USER, K_FOREVER);
57 
58 	k_object_access_grant(&start_sem, tid);
59 	k_object_access_grant(&end_sem, tid);
60 
61 	k_thread_start(tid);
62 
63 	k_sem_give(&start_sem);
64 
65 	zassert_true(k_sem_take(&end_sem, K_SECONDS(1)) == 0,
66 		     "k_sem_take(end_sem) failed");
67 
68 	k_thread_abort(tid);
69 
70 	k_object_release(dyn_thread);
71 }
72 
permission_test(void)73 static void permission_test(void)
74 {
75 	k_tid_t tid;
76 
77 	dyn_thread = k_object_alloc(K_OBJ_THREAD);
78 
79 	zassert_not_null(dyn_thread, "Cannot allocate thread k_object!");
80 
81 	tid = k_thread_create(dyn_thread, dyn_thread_stack, STACKSIZE,
82 			      dyn_thread_entry, NULL, NULL, NULL,
83 			      K_PRIO_PREEMPT(0), K_USER, K_FOREVER);
84 
85 	k_object_access_grant(&start_sem, tid);
86 
87 	k_thread_start(tid);
88 
89 	/*
90 	 * Notice dyn_thread will not have permission to access
91 	 * end_sem, which will cause kernel oops.
92 	 */
93 
94 	k_sem_give(&start_sem);
95 
96 	/*
97 	 * If dyn_thread has permission to access end_sem,
98 	 * k_sem_take() would be able to take the semaphore.
99 	 */
100 	zassert_true(k_sem_take(&end_sem, K_SECONDS(1)) != 0,
101 		     "Semaphore end_sem has incorrect permission");
102 
103 	k_thread_abort(tid);
104 
105 	k_object_release(dyn_thread);
106 }
107 
108 /**
109  * @ingroup kernel_thread_tests
110  * @brief Test object permission on dynamic user thread when index is reused
111  *
112  * @details This creates one dynamic thread with permissions to both
113  * semaphores so there is no fault. Then a new thread is created and will be
114  * re-using the thread index in first pass. Except the second thread does
115  * not have permission to one of the semaphore. If permissions are cleared
116  * correctly when thread is destroyed, the second should raise kernel oops.
117  */
ZTEST(thread_dynamic,test_dyn_thread_perms)118 ZTEST(thread_dynamic, test_dyn_thread_perms)
119 {
120 	if (!(IS_ENABLED(CONFIG_USERSPACE))) {
121 		ztest_test_skip();
122 	}
123 
124 	permission_test();
125 
126 	TC_PRINT("===== must have access denied on k_sem %p\n", &end_sem);
127 }
128 
ZTEST(thread_dynamic,test_thread_index_management)129 ZTEST(thread_dynamic, test_thread_index_management)
130 {
131 	int i, ctr = 0;
132 
133 	/* Create thread objects until we run out of ids */
134 	while (true) {
135 		struct k_thread *t = k_object_alloc(K_OBJ_THREAD);
136 
137 		if (t == NULL) {
138 			break;
139 		}
140 
141 		dynamic_threads[ctr] = t;
142 		ctr++;
143 	}
144 
145 	zassert_true(ctr != 0, "unable to create any thread objects");
146 
147 	TC_PRINT("created %d thread objects\n", ctr);
148 
149 	/* Show that the above NULL return value wasn't because we ran out of
150 	 * heap space. For that we need to duplicate how objects are allocated
151 	 * in kernel/userspace.c. We pessimize the alignment to the worst
152 	 * case to simplify things somewhat.
153 	 */
154 	size_t ret = 1024 * 1024;  /* sure-to-fail initial value */
155 	void *blob;
156 
157 	switch (K_OBJ_THREAD) {
158 	/** @cond keep_doxygen_away */
159 	#include <zephyr/otype-to-size.h>
160 	/** @endcond */
161 	}
162 	blob = k_object_create_dynamic_aligned(16, ret);
163 	zassert_true(blob != NULL, "out of heap memory");
164 
165 	/* Free one of the threads... */
166 	k_object_free(dynamic_threads[0]);
167 
168 	/* And show that we can now create another one, the freed thread's
169 	 * index should have been garbage collected.
170 	 */
171 	dynamic_threads[0] = k_object_alloc(K_OBJ_THREAD);
172 	zassert_true(dynamic_threads[0] != NULL,
173 		     "couldn't create thread object\n");
174 
175 	/* TODO: Implement a test that shows that thread IDs are properly
176 	 * recycled when a thread object is garbage collected due to references
177 	 * dropping to zero. For example, we ought to be able to exit here
178 	 * without calling k_object_free() on any of the threads we created
179 	 * here; their references would drop to zero and they would be
180 	 * automatically freed. However, it is known that the thread IDs are
181 	 * not properly recycled when this happens, see #17023.
182 	 */
183 	for (i = 0; i < ctr; i++) {
184 		k_object_free(dynamic_threads[i]);
185 	}
186 }
187 
188 /**
189  * @ingroup kernel_thread_tests
190  * @brief Test creation of dynamic user thread under kernel thread
191  *
192  * @details This is a simple test to create a user thread
193  * dynamically via k_object_alloc() under a kernel thread.
194  */
ZTEST(thread_dynamic,test_kernel_create_dyn_user_thread)195 ZTEST(thread_dynamic, test_kernel_create_dyn_user_thread)
196 {
197 	if (!(IS_ENABLED(CONFIG_USERSPACE))) {
198 		ztest_test_skip();
199 	}
200 
201 	create_dynamic_thread();
202 }
203 
204 /**
205  * @ingroup kernel_thread_tests
206  * @brief Test creation of dynamic user thread under user thread
207  *
208  * @details This is a simple test to create a user thread
209  * dynamically via k_object_alloc() under a user thread.
210  */
ZTEST_USER(thread_dynamic,test_user_create_dyn_user_thread)211 ZTEST_USER(thread_dynamic, test_user_create_dyn_user_thread)
212 {
213 	create_dynamic_thread();
214 }
215 
216 /* test case main entry */
thread_test_setup(void)217 void *thread_test_setup(void)
218 {
219 	k_thread_system_pool_assign(k_current_get());
220 
221 	prep();
222 
223 	return NULL;
224 }
225 
226 ZTEST_SUITE(thread_dynamic, NULL, thread_test_setup, NULL, NULL, NULL);
227