@@ -9,6 +9,179 @@ are defined, and how memory may be managed to support user mode threads.
9
9
For details on creating threads that run in user mode, please see
10
10
:ref: `lifecycle_v2 `.
11
11
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
+
12
185
.. toctree ::
13
186
:maxdepth: 2
14
187
0 commit comments