Skip to content

Commit 578e6fd

Browse files
Andrew Boieandrewboie
authored andcommitted
doc: user mode overview documentation
The existing docs immediately dive into the details without providing the user with any kind of high level overview or description of the threat model. Signed-off-by: Andrew Boie <[email protected]>
1 parent afc7877 commit 578e6fd

File tree

1 file changed

+173
-0
lines changed

1 file changed

+173
-0
lines changed

doc/kernel/usermode/usermode.rst

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,179 @@ are defined, and how memory may be managed to support user mode threads.
99
For details on creating threads that run in user mode, please see
1010
:ref:`lifecycle_v2`.
1111

12+
Threat Model
13+
============
14+
15+
User mode threads are considered to be untrusted by Zephyr and are therefore
16+
isolated from other user mode threads and from the kernel. A flawed or
17+
malicious user mode thread cannot leak or modify the private data/resources
18+
of another thread or the kernel, and cannot interfere with or
19+
control another user mode thread or the kernel.
20+
21+
Example use-cases of Zephyr's user mode features:
22+
23+
- The kernel can protect against many unintentional programming errors which
24+
could otherwise silently or spectacularly corrupt the system.
25+
26+
- The kernel can sandbox complex data parsers such as interpreters, network
27+
protocols, and filesystems such that malicious third-party code or data
28+
cannot compromise the kernel or other threads.
29+
30+
- The kernel can support the notion of multiple logical "applications", each
31+
with their own group of threads and private data structures, which are
32+
isolated from each other if one crashes or is otherwise compromised.
33+
34+
Design Goals
35+
------------
36+
37+
For threads running in a non-privileged CPU state (hereafter referred to as
38+
'user mode') we aim to protect against the following:
39+
40+
- We prevent access to memory not specifically granted, or incorrect access to
41+
memory that has an incompatible policy, such as attempting to write to a
42+
read-only area.
43+
44+
- Threads are automatically granted access to their own stack memory
45+
region, and all other stacks are inaccessible.
46+
47+
- By default, program text and read-only data are accessible to all threads
48+
on read-only basis, kernel-wide. This policy may be adjusted.
49+
50+
- If the optional "application memory" feature is enabled, then all
51+
non-kernel globals defined in the application and libraries will be
52+
accessible.
53+
54+
- We prevent use of device drivers or kernel objects not specifically granted,
55+
with the permission granularity on a per object or per driver instance
56+
basis.
57+
58+
- We validate kernel or driver API calls with incorrect parameters that would
59+
otherwise cause a crash or corruption of data structures private to the
60+
kernel. This includes:
61+
62+
- Using the wrong kernel object type.
63+
64+
- Using parameters outside of proper bounds or with nonsensical values.
65+
66+
- Passing memory buffers that the calling thread does not have sufficient
67+
access to read or write, depending on the semantics of the API.
68+
69+
- Use of kernel objects that are not in a proper initialization state.
70+
71+
- We ensure the detection and safe handling of user mode stack overflows.
72+
73+
- We prevent invoking system calls to functions excluded by the kernel
74+
configuration.
75+
76+
- We prevent disabling of or tampering with kernel-defined and hardware-
77+
enforced memory protections.
78+
79+
- We prevent re-entry from user to supervisor mode except through the kernel-
80+
defined system calls and interrupt handlers.
81+
82+
- We prevent the introduction of new executable code by user mode threads,
83+
except to the extent to which this is supported by kernel system calls.
84+
85+
We are specifically not protecting against the following attacks:
86+
87+
- The kernel itself, and any threads that are executing in supervisor mode,
88+
are assumed to be trusted.
89+
90+
- The toolchain and any supplemental programs used by the build system are
91+
assumed to be trusted.
92+
93+
- The kernel build is assumed to be trusted. There is considerable build-time
94+
logic for creating the tables of valid kernel objects, defining system calls,
95+
and configuring interrupts. The .elf binary files that are worked with
96+
during this process are all assumed to be trusted code.
97+
98+
- We can't protect against mistakes made in memory domain configuration done in
99+
kernel mode that exposes private kernel data structures to a user thread. RAM
100+
for kernel objects should always be configured as supervisor-only.
101+
102+
- It is possible to make top-level declarations of user mode threads and
103+
assign them permissions to kernel objects. In general, all C and header
104+
files that are part of the kernel build producing zephyr.elf are assumed to
105+
be trusted.
106+
107+
- We do not protect against denial of service attacks through thread CPU
108+
starvation. Zephyr has no thread priority aging and a user thread of a
109+
particular priority can starve all threads of lower priority, and also other
110+
threads of the same priority if time-slicing is not enabled.
111+
112+
- There are build-time defined limits on how many threads can be active
113+
simultaneously, after which creation of new user threads will fail.
114+
115+
- Stack overflows for threads running in supervisor mode may be caught,
116+
but the integrity of the system cannot be guaranteed.
117+
118+
High-level Policy Details
119+
=========================
120+
121+
Broadly speaking, we accomplish these thread-level memory protection goals
122+
through the following mechanisms:
123+
124+
- Any user thread will only have access to its own stack memory by default.
125+
Access to any other RAM will need to be done on the thread's behalf through
126+
system calls, or specifically granted by a supervisor thread using the
127+
:ref:`memory_domain` APIs. Newly created threads inherit the memory domain
128+
configuration of the parent. Threads may communicate with each other
129+
by having shared membership of the same memory domains, or via kernel objects
130+
such as semaphores and pipes.
131+
132+
- If the optional :option:`CONFIG_APPLICATION_MEMORY` feature is enabled, all
133+
threads will have read/write access to non-kernel globals.
134+
135+
- User threads cannot directly access memory belonging to kernel objects.
136+
Although pointers to kernel objects are used to reference them, actual
137+
manipulation of kernel objects is done through system call interfaces. Device
138+
drivers and threads stacks are also considered kernel objects. This ensures
139+
that any data inside a kernel object that is private to the kernel cannot be
140+
tampered with.
141+
142+
- User threads by default have no permission to access any kernel object or
143+
driver other than their own thread object. Such access must be granted by
144+
another thread that is either in supervisor mode or has permission on both
145+
the receiving thread object and the kernel object being granted access to.
146+
The creation of new threads has an option to automatically inherit
147+
permissions of all kernel objects granted to the parent, except the parent
148+
thread itself.
149+
150+
- For performance and footprint reasons Zephyr normally does little or no
151+
parameter error checking for kernel object or device driver APIs. Access from
152+
user mode through system calls involves an extra layer of handler functions,
153+
which are expected to rigorously validate access permissions and type of
154+
the object, check the validity of other parameters through bounds checking or
155+
other means, and verify proper read/write access to any memory buffers
156+
involved.
157+
158+
- Thread stacks are defined in such a way that exceeding the specified stack
159+
space will generate a hardware fault. The way this is done specifically
160+
varies per architecture.
161+
162+
Constraints
163+
===========
164+
165+
All kernel objects, thread stacks, and device driver instances must be defined
166+
at build time if they are to be used from user mode. Dynamic use-cases for
167+
kernel objects will need to go through pre-defined pools of available objects.
168+
169+
There are some constraints if additional application binary data is loaded
170+
for execution after the kernel starts:
171+
172+
- Loaded object code will not be able to define any kernel objects that will be
173+
recognized by the kernel. This code will instead need to use APIs for
174+
requesting kernel objects from pools.
175+
176+
- Similarly, since the loaded object code will not be part of the kernel build
177+
process, this code will not be able to install interrupt handlers,
178+
instantiate device drivers, or define system calls, regardless of what
179+
mode it runs in.
180+
181+
- Loaded object code that does not come from a verified source should always
182+
be entered with the CPU already in user mode.
183+
184+
12185
.. toctree::
13186
:maxdepth: 2
14187

0 commit comments

Comments
 (0)