Skip to content

Commit 2055d75

Browse files
stephensmalleyAnas Nashif
authored andcommitted
tests/kernel/mem_protect/userspace: Add userspace protection tests
This is still work-in-progress, but putting it up in case it is helpful to people working in this area and for early comments. Add a set of tests to validate the expected security properties of threads created with K_USER when CONFIG_USERSPACE=y. This can be used as a regression test for architectures that already implement this support and as a validation test for others. I considered incorporating these tests into the existing protection test, but decided against it since protection does not enable or rely upon CONFIG_USERSPACE for its existing tests and passes on everything that provides MPU or MMU support, even without full userspace support. I also considered incorporating these tests into the existing obj_validation test, but decided against it since obj_validation only tests the object validation/permission logic, does not run any user mode threads (or strictly depend on that support), and passes on both x86 and arm today, unlike these tests. That said, I have no strong objections if it would be preferable to fold these into it (and perhaps rename it to be more general). The current tests implemented in this test program verify the following for a thread created with K_USER: is_usermode: is running in usermode priv_insn: cannot invoke privileged insns directly write_control: cannot write to control registers disable_mmu_mpu: cannot disable memory protections (MMU/MPU) read_kernram: cannot read from kernel RAM write_kernram: cannot write to kernel RAM write_kernro: cannot write to kernel rodata write_kerntext: cannot write to kernel text read_kernel_data: cannot read __kernel-marked data write_kernel_data: cannot write __kernel-marked data read_kernel_stack: cannot read the kernel/privileged stack write_kernel_stack: cannot write the kernel/privileged stack pass_user_object: cannot pass a non-kernel object to a syscall pass_noperms_object: cannot pass an object to a syscall without a grant start_kernel_thread: cannot start a kernel (non-user) thread Some of the tests overlap and could possibly be dropped, but it seems harmless to retain them. The particular targets of read/write tests are arbitrary other than meeting the test criteria and can be changed (e.g. in data, rodata, or text) if desired to avoid coupling to kernel implementation details that may change in the future. On qemu_x86, all of the tests pass. And, if you replace all occurrences of ztest_user_unit_test() with ztest_unit_test(), then all of the tests fail (i.e. when the tests are run in kernel mode, they all fail as expected). On frdm_k64f presently (w/o the arm userspace patches), all of the tests fail except for write_kernro and write_kerntext, as expected. ToDo: - Verify that a user thread cannot access data in another memory domain. - Verify that a user thread cannot access another thread's stack. - Verify that a user thread cannot access another thread's kobject. - Verify that k_thread_user_mode_enter() transitions correctly. - Verify that k_object_access_revoke() is enforced. - Verify that syscalls return to user mode upon completion. - Verify that a user thread cannot abuse other svc calls (ARM-specific). - Other suggested properties we should be testing? Signed-off-by: Stephen Smalley <[email protected]>
1 parent 6caf696 commit 2055d75

File tree

4 files changed

+247
-0
lines changed

4 files changed

+247
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
2+
project(NONE)
3+
4+
FILE(GLOB app_sources src/*.c)
5+
target_sources(app PRIVATE ${app_sources})
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
CONFIG_ZTEST=y
2+
CONFIG_USERSPACE=y
3+
CONFIG_APPLICATION_MEMORY=y
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
/*
2+
* Parts derived from tests/kernel/fatal/src/main.c, which has the
3+
* following copyright and license:
4+
*
5+
* Copyright (c) 2017 Intel Corporation
6+
*
7+
* SPDX-License-Identifier: Apache-2.0
8+
*/
9+
10+
#include <zephyr.h>
11+
#include <ztest.h>
12+
#include <kernel_structs.h>
13+
#include <string.h>
14+
#include <stdlib.h>
15+
16+
#define INFO(fmt, ...) printk(fmt, ##__VA_ARGS__)
17+
18+
/* ARM is a special case, in that k_thread_abort() does indeed return
19+
* instead of calling _Swap() directly. The PendSV exception is queued
20+
* and immediately fires upon completing the exception path; the faulting
21+
* thread is never run again.
22+
*/
23+
#ifndef CONFIG_ARM
24+
FUNC_NORETURN
25+
#endif
26+
void _SysFatalErrorHandler(unsigned int reason, const NANO_ESF *pEsf)
27+
{
28+
INFO("Caught system error -- reason %d\n", reason);
29+
ztest_test_pass();
30+
#ifndef CONFIG_ARM
31+
CODE_UNREACHABLE;
32+
#endif
33+
}
34+
35+
static void is_usermode(void)
36+
{
37+
/* Confirm that we are in fact running in user mode. */
38+
zassert_true(_is_user_context(), "thread left in kernel mode\n");
39+
}
40+
41+
static void priv_insn(void)
42+
{
43+
/* Try to invoke what should require a privileged instruction. */
44+
unsigned int key = irq_lock();
45+
46+
irq_unlock(key); /* just in case we succeeded */
47+
zassert_unreachable("irq_lock did not fault\n");
48+
}
49+
50+
static void write_control(void)
51+
{
52+
/* Try to write to a control register. */
53+
#if defined(CONFIG_X86)
54+
__asm__ volatile (
55+
"mov %cr0, %eax;\n\t"
56+
"and $0xfffeffff, %eax;\n\t"
57+
"mov %eax, %cr0;\n\t"
58+
);
59+
#elif defined(CONFIG_ARM)
60+
__asm__ volatile (
61+
"mrs r2, CONTROL;\n\t"
62+
"bic r2, #1;\n\t"
63+
"msr CONTROL, r2;\n\t"
64+
);
65+
#else
66+
#error "Not implemented for this architecture"
67+
#endif
68+
zassert_unreachable("Write to control register did not fault\n");
69+
}
70+
71+
static void disable_mmu_mpu(void)
72+
{
73+
/* Try to disable memory protections. */
74+
#if defined(CONFIG_X86)
75+
__asm__ volatile (
76+
"mov %cr0, %eax;\n\t"
77+
"and $0x7ffeffff, %eax;\n\t"
78+
"mov %eax, %cr0;\n\t"
79+
);
80+
#elif defined(CONFIG_ARM)
81+
arm_core_mpu_disable();
82+
#else
83+
#error "Not implemented for this architecture"
84+
#endif
85+
zassert_unreachable("Disable MMU/MPU did not fault\n");
86+
}
87+
88+
static void read_kernram(void)
89+
{
90+
/* Try to read from kernel RAM. */
91+
void *p = _current->init_data;
92+
93+
printk("%p\n", p);
94+
zassert_unreachable("Read from kernel RAM did not fault\n");
95+
}
96+
97+
static void write_kernram(void)
98+
{
99+
/* Try to write to kernel RAM. */
100+
_current->init_data = NULL;
101+
zassert_unreachable("Write to kernel RAM did not fault\n");
102+
}
103+
104+
extern int _k_neg_eagain;
105+
106+
static void write_kernro(void)
107+
{
108+
/* Try to write to kernel RO. */
109+
_k_neg_eagain = -EINVAL;
110+
zassert_unreachable("Write to kernel RO did not fault\n");
111+
}
112+
113+
static void write_kerntext(void)
114+
{
115+
/* Try to write to kernel text. */
116+
memcpy(&_is_thread_essential, 0, 4);
117+
zassert_unreachable("Write to kernel text did not fault\n");
118+
}
119+
120+
__kernel static int kernel_data;
121+
122+
static void read_kernel_data(void)
123+
{
124+
/* Try to read from embedded kernel data. */
125+
int value = kernel_data;
126+
127+
printk("%d\n", value);
128+
zassert_unreachable("Read from __kernel data did not fault\n");
129+
}
130+
131+
static void write_kernel_data(void)
132+
{
133+
kernel_data = 1;
134+
zassert_unreachable("Write to __kernel data did not fault\n");
135+
}
136+
137+
/*
138+
* volatile to avoid compiler mischief.
139+
*/
140+
volatile int *ptr = NULL;
141+
#if defined(CONFIG_X86)
142+
volatile size_t size = MMU_PAGE_SIZE;
143+
#elif defined(CONFIG_ARM) && defined(CONFIG_PRIVILEGED_STACK_SIZE)
144+
volatile size_t size = CONFIG_PRIVILEGED_STACK_SIZE;
145+
#else
146+
volatile size_t size = 512;
147+
#endif
148+
149+
static void read_kernel_stack(void)
150+
{
151+
/* Try to read from kernel stack. */
152+
int s[1];
153+
154+
s[0] = 0;
155+
ptr = &s[0];
156+
ptr = (int *)((unsigned char *)ptr - size);
157+
printk("%d\n", *ptr);
158+
zassert_unreachable("Read from kernel stack did not fault\n");
159+
}
160+
161+
static void write_kernel_stack(void)
162+
{
163+
/* Try to write to kernel stack. */
164+
int s[1];
165+
166+
s[0] = 0;
167+
ptr = &s[0];
168+
ptr = (int *)((unsigned char *)ptr - size);
169+
*ptr = 42;
170+
zassert_unreachable("Write to kernel stack did not fault\n");
171+
}
172+
173+
174+
static struct k_sem sem;
175+
176+
static void pass_user_object(void)
177+
{
178+
/* Try to pass a user object to a system call. */
179+
k_sem_init(&sem, 0, 1);
180+
zassert_unreachable("Pass a user object to a syscall did not fault\n");
181+
}
182+
183+
__kernel static struct k_sem ksem;
184+
185+
static void pass_noperms_object(void)
186+
{
187+
/* Try to pass a object to a system call w/o permissions. */
188+
k_sem_init(&ksem, 0, 1);
189+
zassert_unreachable("Pass an unauthorized object to a "
190+
"syscall did not fault\n");
191+
}
192+
193+
__kernel struct k_thread kthread_thread;
194+
195+
#define STACKSIZE 512
196+
K_THREAD_STACK_DEFINE(kthread_stack, STACKSIZE);
197+
198+
void thread_body(void)
199+
{
200+
}
201+
202+
static void start_kernel_thread(void)
203+
{
204+
/* Try to start a kernel thread from a usermode thread */
205+
k_thread_create(&kthread_thread, kthread_stack, STACKSIZE,
206+
(k_thread_entry_t)thread_body, NULL, NULL, NULL,
207+
K_PRIO_PREEMPT(1), K_INHERIT_PERMS,
208+
K_NO_WAIT);
209+
zassert_unreachable("Create a kernel thread did not fault\n");
210+
}
211+
212+
void test_main(void)
213+
{
214+
k_thread_access_grant(k_current_get(),
215+
&kthread_thread, &kthread_stack,
216+
NULL);
217+
ztest_test_suite(test_userspace,
218+
ztest_user_unit_test(is_usermode),
219+
ztest_user_unit_test(priv_insn),
220+
ztest_user_unit_test(write_control),
221+
ztest_user_unit_test(disable_mmu_mpu),
222+
ztest_user_unit_test(read_kernram),
223+
ztest_user_unit_test(write_kernram),
224+
ztest_user_unit_test(write_kernro),
225+
ztest_user_unit_test(write_kerntext),
226+
ztest_user_unit_test(read_kernel_data),
227+
ztest_user_unit_test(write_kernel_data),
228+
ztest_user_unit_test(read_kernel_stack),
229+
ztest_user_unit_test(write_kernel_stack),
230+
ztest_user_unit_test(pass_user_object),
231+
ztest_user_unit_test(pass_noperms_object),
232+
ztest_user_unit_test(start_kernel_thread)
233+
);
234+
ztest_run_test_suite(test_userspace);
235+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
tests:
2+
- test:
3+
filter: CONFIG_ARCH_HAS_USERSPACE
4+
tags: core security userspace ignore_faults

0 commit comments

Comments
 (0)