1 /*
2 * Copyright (c) 2021 Stephanos Ioannidis <root@stephanos.io>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /*
8 * @file Newlib thread-safety lock test
9 *
10 * This file contains a set of tests to verify that the newlib retargetable
11 * locking interface is functional and the internal newlib locks function as
12 * intended.
13 */
14
15 #include <zephyr.h>
16 #include <ztest.h>
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <malloc.h>
21 #include <envlock.h>
22
23 #define STACK_SIZE (512 + CONFIG_TEST_EXTRA_STACKSIZE)
24
25 #ifdef CONFIG_USERSPACE
26 #define THREAD_OPT (K_USER | K_INHERIT_PERMS)
27 #else
28 #define THREAD_OPT (0)
29 #endif /* CONFIG_USERSPACE */
30
31 static struct k_thread tdata;
32 static K_THREAD_STACK_DEFINE(tstack, STACK_SIZE);
33
34 /* Newlib internal lock functions */
35 extern void __sfp_lock_acquire(void);
36 extern void __sfp_lock_release(void);
37 extern void __sinit_lock_acquire(void);
38 extern void __sinit_lock_release(void);
39 extern void __tz_lock(void);
40 extern void __tz_unlock(void);
41
42 /* Static locks */
43 extern struct k_mutex __lock___sinit_recursive_mutex;
44 extern struct k_mutex __lock___sfp_recursive_mutex;
45 extern struct k_mutex __lock___atexit_recursive_mutex;
46 extern struct k_mutex __lock___malloc_recursive_mutex;
47 extern struct k_mutex __lock___env_recursive_mutex;
48 extern struct k_sem __lock___at_quick_exit_mutex;
49 extern struct k_sem __lock___tz_mutex;
50 extern struct k_sem __lock___dd_hash_mutex;
51 extern struct k_sem __lock___arc4random_mutex;
52
53 /**
54 * @brief Test retargetable locking non-recursive (semaphore) interface
55 *
56 * This test verifies that a non-recursive lock (semaphore) can be dynamically
57 * created, acquired, released and closed through the retargetable locking
58 * interface.
59 */
test_retargetable_lock_sem(void)60 static void test_retargetable_lock_sem(void)
61 {
62 _LOCK_T lock = NULL;
63
64 /* Dynamically allocate and initialise a new lock */
65 __retarget_lock_init(&lock);
66 zassert_not_null(lock, "non-recursive lock init failed");
67
68 /* Acquire lock and verify acquisition */
69 __retarget_lock_acquire(lock);
70 zassert_equal(__retarget_lock_try_acquire(lock), 0,
71 "non-recursive lock acquisition failed");
72
73 /* Release lock and verify release */
74 __retarget_lock_release(lock);
75 zassert_not_equal(__retarget_lock_try_acquire(lock), 0,
76 "non-recursive lock release failed");
77
78 /* Close and deallocate lock */
79 __retarget_lock_close(lock);
80 }
81
retargetable_lock_mutex_thread_acq(void * p1,void * p2,void * p3)82 static void retargetable_lock_mutex_thread_acq(void *p1, void *p2, void *p3)
83 {
84 _LOCK_T lock = p1;
85 int ret;
86
87 /*
88 * Attempt to lock the recursive lock from child thread and verify
89 * that it fails.
90 */
91 ret = __retarget_lock_try_acquire_recursive(lock);
92 zassert_equal(ret, 0, "recursive lock acquisition failed");
93 }
94
retargetable_lock_mutex_thread_rel(void * p1,void * p2,void * p3)95 static void retargetable_lock_mutex_thread_rel(void *p1, void *p2, void *p3)
96 {
97 _LOCK_T lock = p1;
98 int ret;
99
100 /*
101 * Attempt to lock the recursive lock from child thread and verify
102 * that it fails.
103 */
104 ret = __retarget_lock_try_acquire_recursive(lock);
105 zassert_not_equal(ret, 0, "recursive lock release failed");
106 }
107
108 /**
109 * @brief Test retargetable locking recursive (mutex) interface
110 *
111 * This test verifies that a recursive lock (mutex) can be dynamically created,
112 * acquired, released, and closed through the retargetable locking interface.
113 */
test_retargetable_lock_mutex(void)114 static void test_retargetable_lock_mutex(void)
115 {
116 _LOCK_T lock = NULL;
117 k_tid_t tid;
118
119 /* Dynamically allocate and initialise a new lock */
120 __retarget_lock_init_recursive(&lock);
121 zassert_not_null(lock, "recursive lock init failed");
122
123 /* Acquire lock from parent thread */
124 __retarget_lock_acquire_recursive(lock);
125
126 /* Spawn a lock acquisition check thread and wait for exit */
127 tid = k_thread_create(&tdata, tstack, STACK_SIZE,
128 retargetable_lock_mutex_thread_acq, lock,
129 NULL, NULL, K_PRIO_PREEMPT(0), THREAD_OPT,
130 K_NO_WAIT);
131
132 k_thread_join(tid, K_FOREVER);
133
134 /* Release lock from parent thread */
135 __retarget_lock_release_recursive(lock);
136
137 /* Spawn a lock release check thread and wait for exit */
138 tid = k_thread_create(&tdata, tstack, STACK_SIZE,
139 retargetable_lock_mutex_thread_rel, lock,
140 NULL, NULL, K_PRIO_PREEMPT(0), THREAD_OPT,
141 K_NO_WAIT);
142
143 k_thread_join(tid, K_FOREVER);
144
145 /* Close and deallocate lock */
146 __retarget_lock_close_recursive(lock);
147 }
148
sinit_lock_thread_acq(void * p1,void * p2,void * p3)149 static void sinit_lock_thread_acq(void *p1, void *p2, void *p3)
150 {
151 int ret;
152
153 /*
154 * Attempt to lock the sinit mutex from child thread using
155 * retargetable locking interface. This operation should fail if the
156 * __sinit_lock_acquire() implementation internally uses the
157 * retargetable locking interface.
158 */
159 ret = __retarget_lock_try_acquire_recursive(
160 (_LOCK_T)&__lock___sinit_recursive_mutex);
161
162 zassert_equal(ret, 0, "__sinit_lock_acquire() is not using "
163 "retargetable locking interface");
164 }
165
sinit_lock_thread_rel(void * p1,void * p2,void * p3)166 static void sinit_lock_thread_rel(void *p1, void *p2, void *p3)
167 {
168 int ret;
169
170 /*
171 * Attempt to lock the sinit mutex from child thread using
172 * retargetable locking interface. This operation should succeed if the
173 * __sinit_lock_release() implementation internally uses the
174 * retargetable locking interface.
175 */
176 ret = __retarget_lock_try_acquire_recursive(
177 (_LOCK_T)&__lock___sinit_recursive_mutex);
178
179 zassert_not_equal(ret, 0, "__sinit_lock_release() is not using "
180 "retargetable locking interface");
181
182 /* Release sinit lock */
183 __retarget_lock_release_recursive(
184 (_LOCK_T)&__lock___sinit_recursive_mutex);
185 }
186
187 /**
188 * @brief Test sinit lock functions
189 *
190 * This test calls the __sinit_lock_acquire() and __sinit_lock_release()
191 * functions to verify that sinit lock is functional and its implementation
192 * is provided by the retargetable locking interface.
193 */
test_sinit_lock(void)194 static void test_sinit_lock(void)
195 {
196 k_tid_t tid;
197
198 /* Lock the sinit mutex from parent thread */
199 __sinit_lock_acquire();
200
201 /* Spawn a lock check thread and wait for exit */
202 tid = k_thread_create(&tdata, tstack, STACK_SIZE,
203 sinit_lock_thread_acq, NULL, NULL, NULL,
204 K_PRIO_PREEMPT(0), THREAD_OPT, K_NO_WAIT);
205
206 k_thread_join(tid, K_FOREVER);
207
208 /* Unlock the sinit mutex from parent thread */
209 __sinit_lock_release();
210
211 /* Spawn an unlock check thread and wait for exit */
212 tid = k_thread_create(&tdata, tstack, STACK_SIZE,
213 sinit_lock_thread_rel, NULL, NULL, NULL,
214 K_PRIO_PREEMPT(0), THREAD_OPT, K_NO_WAIT);
215
216 k_thread_join(tid, K_FOREVER);
217 }
218
sfp_lock_thread_acq(void * p1,void * p2,void * p3)219 static void sfp_lock_thread_acq(void *p1, void *p2, void *p3)
220 {
221 int ret;
222
223 /*
224 * Attempt to lock the sfp mutex from child thread using retargetable
225 * locking interface. This operation should fail if the
226 * __sfp_lock_acquire() implementation internally uses the retargetable
227 * locking interface.
228 */
229 ret = __retarget_lock_try_acquire_recursive(
230 (_LOCK_T)&__lock___sfp_recursive_mutex);
231
232 zassert_equal(ret, 0, "__sfp_lock_acquire() is not using "
233 "retargetable locking interface");
234 }
235
sfp_lock_thread_rel(void * p1,void * p2,void * p3)236 static void sfp_lock_thread_rel(void *p1, void *p2, void *p3)
237 {
238 int ret;
239
240 /*
241 * Attempt to lock the sfp mutex from child thread using retargetable
242 * locking interface. This operation should succeed if the
243 * __sfp_lock_release() implementation internally uses the retargetable
244 * locking interface.
245 */
246 ret = __retarget_lock_try_acquire_recursive(
247 (_LOCK_T)&__lock___sfp_recursive_mutex);
248
249 zassert_not_equal(ret, 0, "__sfp_lock_release() is not using "
250 "retargetable locking interface");
251
252 /* Release sfp lock */
253 __retarget_lock_release_recursive(
254 (_LOCK_T)&__lock___sfp_recursive_mutex);
255 }
256
257 /**
258 * @brief Test sfp lock functions
259 *
260 * This test calls the __sfp_lock_acquire() and __sfp_lock_release() functions
261 * to verify that sfp lock is functional and its implementation is provided by
262 * the retargetable locking interface.
263 */
test_sfp_lock(void)264 static void test_sfp_lock(void)
265 {
266 k_tid_t tid;
267
268 /* Lock the sfp mutex from parent thread */
269 __sfp_lock_acquire();
270
271 /* Spawn a lock check thread and wait for exit */
272 tid = k_thread_create(&tdata, tstack, STACK_SIZE,
273 sfp_lock_thread_acq, NULL, NULL, NULL,
274 K_PRIO_PREEMPT(0), THREAD_OPT, K_NO_WAIT);
275
276 k_thread_join(tid, K_FOREVER);
277
278 /* Unlock the sfp mutex from parent thread */
279 __sfp_lock_release();
280
281 /* Spawn an unlock check thread and wait for exit */
282 tid = k_thread_create(&tdata, tstack, STACK_SIZE,
283 sfp_lock_thread_rel, NULL, NULL, NULL,
284 K_PRIO_PREEMPT(0), THREAD_OPT, K_NO_WAIT);
285
286 k_thread_join(tid, K_FOREVER);
287 }
288
malloc_lock_thread_lock(void * p1,void * p2,void * p3)289 static void malloc_lock_thread_lock(void *p1, void *p2, void *p3)
290 {
291 int ret;
292
293 /*
294 * Attempt to lock the malloc mutex from child thread using
295 * retargetable locking interface. This operation should fail if the
296 * __malloc_lock() implementation internally uses the retargetable
297 * locking interface.
298 */
299 ret = __retarget_lock_try_acquire_recursive(
300 (_LOCK_T)&__lock___malloc_recursive_mutex);
301
302 zassert_equal(ret, 0, "__malloc_lock() is not using retargetable "
303 "locking interface");
304 }
305
malloc_lock_thread_unlock(void * p1,void * p2,void * p3)306 static void malloc_lock_thread_unlock(void *p1, void *p2, void *p3)
307 {
308 int ret;
309
310 /*
311 * Attempt to lock the malloc mutex from child thread using
312 * retargetable locking interface. This operation should succeed if the
313 * __malloc_unlock() implementation internally uses the retargetable
314 * locking interface.
315 */
316 ret = __retarget_lock_try_acquire_recursive(
317 (_LOCK_T)&__lock___malloc_recursive_mutex);
318
319 zassert_not_equal(ret, 0, "__malloc_unlock() is not using "
320 "retargetable locking interface");
321
322 /* Release malloc lock */
323 __retarget_lock_release_recursive(
324 (_LOCK_T)&__lock___malloc_recursive_mutex);
325 }
326
327 /**
328 * @brief Test malloc lock functions
329 *
330 * This test calls the __malloc_lock() and __malloc_unlock() functions to
331 * verify that malloc lock is functional and its implementation is provided by
332 * the retargetable locking interface.
333 */
test_malloc_lock(void)334 static void test_malloc_lock(void)
335 {
336 k_tid_t tid;
337
338 /* Lock the malloc mutex from parent thread */
339 __malloc_lock(_REENT);
340
341 /* Spawn a lock check thread and wait for exit */
342 tid = k_thread_create(&tdata, tstack, STACK_SIZE,
343 malloc_lock_thread_lock, NULL, NULL, NULL,
344 K_PRIO_PREEMPT(0), THREAD_OPT, K_NO_WAIT);
345
346 k_thread_join(tid, K_FOREVER);
347
348 /* Unlock the malloc mutex from parent thread */
349 __malloc_unlock(_REENT);
350
351 /* Spawn an unlock check thread and wait for exit */
352 tid = k_thread_create(&tdata, tstack, STACK_SIZE,
353 malloc_lock_thread_unlock, NULL, NULL, NULL,
354 K_PRIO_PREEMPT(0), THREAD_OPT, K_NO_WAIT);
355
356 k_thread_join(tid, K_FOREVER);
357 }
358
env_lock_thread_lock(void * p1,void * p2,void * p3)359 static void env_lock_thread_lock(void *p1, void *p2, void *p3)
360 {
361 int ret;
362
363 /*
364 * Attempt to lock the env mutex from child thread using
365 * retargetable locking interface. This operation should fail if the
366 * __env_lock() implementation internally uses the retargetable
367 * locking interface.
368 */
369 ret = __retarget_lock_try_acquire_recursive(
370 (_LOCK_T)&__lock___env_recursive_mutex);
371
372 zassert_equal(ret, 0, "__env_lock() is not using retargetable "
373 "locking interface");
374 }
375
env_lock_thread_unlock(void * p1,void * p2,void * p3)376 static void env_lock_thread_unlock(void *p1, void *p2, void *p3)
377 {
378 int ret;
379
380 /*
381 * Attempt to lock the env mutex from child thread using
382 * retargetable locking interface. This operation should succeed if the
383 * __env_unlock() implementation internally uses the retargetable
384 * locking interface.
385 */
386 ret = __retarget_lock_try_acquire_recursive(
387 (_LOCK_T)&__lock___env_recursive_mutex);
388
389 zassert_not_equal(ret, 0, "__env_unlock() is not using "
390 "retargetable locking interface");
391
392 /* Release env lock */
393 __retarget_lock_release_recursive(
394 (_LOCK_T)&__lock___env_recursive_mutex);
395 }
396
397 /**
398 * @brief Test env lock functions
399 *
400 * This test calls the __env_lock() and __env_unlock() functions to verify
401 * that env lock is functional and its implementation is provided by the
402 * retargetable locking interface.
403 */
test_env_lock(void)404 static void test_env_lock(void)
405 {
406 k_tid_t tid;
407
408 /* Lock the env mutex from parent thread */
409 __env_lock(_REENT);
410
411 /* Spawn a lock check thread and wait for exit */
412 tid = k_thread_create(&tdata, tstack, STACK_SIZE,
413 env_lock_thread_lock, NULL, NULL, NULL,
414 K_PRIO_PREEMPT(0), THREAD_OPT, K_NO_WAIT);
415
416 k_thread_join(tid, K_FOREVER);
417
418 /* Unlock the env mutex from parent thread */
419 __env_unlock(_REENT);
420
421 /* Spawn an unlock check thread and wait for exit */
422 tid = k_thread_create(&tdata, tstack, STACK_SIZE,
423 env_lock_thread_unlock, NULL, NULL, NULL,
424 K_PRIO_PREEMPT(0), THREAD_OPT, K_NO_WAIT);
425
426 k_thread_join(tid, K_FOREVER);
427 }
428
429 /**
430 * @brief Test tz lock functions
431 *
432 * This test calls the __tz_lock() and __tz_unlock() functions to verify that
433 * tz lock is functional and its implementation is provided by the retargetable
434 * locking interface.
435 */
test_tz_lock(void)436 static void test_tz_lock(void)
437 {
438 /* Lock the tz semaphore */
439 __tz_lock();
440
441 /* Attempt to acquire lock and verify failure */
442 zassert_equal(
443 __retarget_lock_try_acquire((_LOCK_T)&__lock___tz_mutex), 0,
444 "__tz_lock() is not using retargetable locking interface");
445
446 /* Unlock the tz semaphore */
447 __tz_unlock();
448
449 /* Attempt to acquire lock and verify success */
450 zassert_not_equal(
451 __retarget_lock_try_acquire((_LOCK_T)&__lock___tz_mutex), 0,
452 "__tz_unlock() is not using retargetable locking interface");
453
454 /* Clean up */
455 __retarget_lock_release((_LOCK_T)&__lock___tz_mutex);
456 }
457
test_newlib_thread_safety_locks(void)458 void test_newlib_thread_safety_locks(void)
459 {
460 #ifdef CONFIG_USERSPACE
461 k_thread_access_grant(k_current_get(), &tdata, &tstack);
462 #endif /* CONFIG_USERSPACE */
463
464 ztest_test_suite(newlib_thread_safety_locks,
465 ztest_user_unit_test(test_retargetable_lock_sem),
466 ztest_user_unit_test(test_retargetable_lock_mutex),
467 ztest_user_unit_test(test_sinit_lock),
468 ztest_user_unit_test(test_sfp_lock),
469 ztest_user_unit_test(test_malloc_lock),
470 ztest_user_unit_test(test_env_lock),
471 ztest_user_unit_test(test_tz_lock));
472
473 ztest_run_test_suite(newlib_thread_safety_locks);
474 }
475