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