1 /*
2  * Copyright The Zephyr Project Contributors
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #ifdef CONFIG_FPU_SHARING
8 #include <math.h>
9 #endif
10 #include <zephyr/ztest.h>
11 #include <zephyr/ztest_error_hook.h>
12 #include <zephyr/syscall_list.h>
13 
14 struct k_thread th0, th1;
15 K_THREAD_STACK_DEFINE(stk0, 2048);
16 K_THREAD_STACK_DEFINE(stk1, 2048);
17 
18 ZTEST_BMEM int attack_stack[128];
19 ZTEST_BMEM uint64_t sys_ret; /* 64 syscalls take result address in r0 */
20 
21 volatile int kernel_secret;
22 volatile int *const attack_sp = &attack_stack[128];
23 const int sysno = K_SYSCALL_K_UPTIME_TICKS;
24 k_tid_t low_tid, hi_tid;
25 
26 struct k_timer timer;
27 volatile ZTEST_BMEM uint64_t hi_thread_runs, test_completed;
28 
k_sys_fatal_error_handler(unsigned int reason,const struct arch_esf * pEsf)29 void k_sys_fatal_error_handler(unsigned int reason, const struct arch_esf *pEsf)
30 {
31 	test_completed = 1;
32 	k_timer_stop(&timer);
33 	ztest_test_pass();
34 	k_thread_abort(low_tid);
35 
36 	/* This check is to handle a case where low prio thread has started and
37 	 * resulted in a fault while changing the sp but
38 	 * the high prio thread is not created yet
39 	 */
40 	if (hi_tid) {
41 		k_thread_abort(hi_tid);
42 	}
43 }
44 
timeout_handler(struct k_timer * timer)45 static void timeout_handler(struct k_timer *timer)
46 {
47 	if (!test_completed) {
48 
49 		printf("hi_thread_runs: %lld\n", hi_thread_runs);
50 		/* the timer times out after 120s,
51 		 * by then hi_fn would have ran multiple times so
52 		 * compare against a random number like 1000 to make sure that
53 		 * hi_fn actually ran for a while
54 		 */
55 		if (hi_thread_runs > 1000) {
56 			ztest_test_pass();
57 		} else {
58 			ztest_test_fail();
59 		}
60 	}
61 }
62 
attack_entry(void)63 void attack_entry(void)
64 {
65 	printf("Call %s from %s\n", __func__, k_is_user_context() ? "user" : "kernel");
66 	/* kernel_secret can only be updated in privilege mode so updating it here should result in
67 	 * a fault. If it doesn't we fail the test.
68 	 */
69 	kernel_secret = 1;
70 
71 	printf("Changed the kernel_secret so marking test as failed\n");
72 	ztest_test_fail();
73 
74 	k_thread_abort(low_tid);
75 	k_thread_abort(hi_tid);
76 }
77 
low_fn(void * arg1,void * arg2,void * arg3)78 void low_fn(void *arg1, void *arg2, void *arg3)
79 {
80 #ifdef CONFIG_FPU_SHARING
81 	double x = 1.2345;
82 	double y = 6.789;
83 
84 	/* some random fp stuff so that an extended stack frame is saved on svc */
85 	zassert_equal(x, 1.2345);
86 	zassert_equal(y, 6.789);
87 #endif
88 	printf("Call %s from %s\n", __func__, k_is_user_context() ? "user" : "kernel");
89 	attack_stack[0] = 1;
90 	__asm__ volatile("mov sp, %0;"
91 			 "1:;"
92 			 "ldr r0, =sys_ret;"
93 			 "ldr r6, =sysno;"
94 			 "ldr r6, [r6];"
95 			 "svc 3;"
96 			 "b 1b;" ::"r"(attack_sp));
97 }
98 
hi_fn(void * arg1,void * arg2,void * arg3)99 void hi_fn(void *arg1, void *arg2, void *arg3)
100 {
101 	printf("Call %s from %s\n", __func__, k_is_user_context() ? "user" : "kernel");
102 	while (1) {
103 		attack_sp[-2] = (int)attack_entry;
104 		k_msleep(1);
105 		hi_thread_runs++;
106 	}
107 }
108 
ZTEST(arm_user_stack_test,test_arm_user_stack_corruption)109 ZTEST(arm_user_stack_test, test_arm_user_stack_corruption)
110 {
111 	k_timer_init(&timer, timeout_handler, NULL);
112 	k_timer_start(&timer, K_SECONDS(120), K_NO_WAIT);
113 
114 	low_tid = k_thread_create(&th0, stk0, K_THREAD_STACK_SIZEOF(stk0), low_fn, NULL, NULL, NULL,
115 				  2,
116 #ifdef CONFIG_FPU_SHARING
117 				  K_INHERIT_PERMS | K_USER | K_FP_REGS,
118 #else
119 				  K_INHERIT_PERMS | K_USER,
120 #endif
121 				  K_NO_WAIT);
122 
123 	k_msleep(6); /* let low_fn start looping */
124 	hi_tid = k_thread_create(&th1, stk1, K_THREAD_STACK_SIZEOF(stk1), hi_fn, NULL, NULL, NULL,
125 				 1, K_INHERIT_PERMS | K_USER, K_NO_WAIT);
126 
127 	k_thread_join(&th0, K_FOREVER);
128 	k_thread_join(&th1, K_FOREVER);
129 }
130 
131 ZTEST_SUITE(arm_user_stack_test, NULL, NULL, NULL, NULL, NULL);
132