|  | .. _usermode_overview: | 
|  |  | 
|  | Overview | 
|  | ######## | 
|  |  | 
|  | Threat Model | 
|  | ************ | 
|  |  | 
|  | User mode threads are considered to be untrusted by Zephyr and are therefore | 
|  | isolated from other user mode threads and from the kernel. A flawed or | 
|  | malicious user mode thread cannot leak or modify the private data/resources | 
|  | of another thread or the kernel, and cannot interfere with or | 
|  | control another user mode thread or the kernel. | 
|  |  | 
|  | Example use-cases of Zephyr's user mode features: | 
|  |  | 
|  | - The kernel can protect against many unintentional programming errors which | 
|  | could otherwise silently or spectacularly corrupt the system. | 
|  |  | 
|  | - The kernel can sandbox complex data parsers such as interpreters, network | 
|  | protocols, and filesystems such that malicious third-party code or data | 
|  | cannot compromise the kernel or other threads. | 
|  |  | 
|  | - The kernel can support the notion of multiple logical "applications", each | 
|  | with their own group of threads and private data structures, which are | 
|  | isolated from each other if one crashes or is otherwise compromised. | 
|  |  | 
|  | Design Goals | 
|  | ============ | 
|  |  | 
|  | For threads running in a non-privileged CPU state (hereafter referred to as | 
|  | 'user mode') we aim to protect against the following: | 
|  |  | 
|  | - We prevent access to memory not specifically granted, or incorrect access to | 
|  | memory that has an incompatible policy, such as attempting to write to a | 
|  | read-only area. | 
|  |  | 
|  | - Access to thread stack buffers will be controlled with a policy which | 
|  | partially depends on the underlying memory protection hardware. | 
|  |  | 
|  | - A user thread will by default have read/write access to its own stack | 
|  | buffer. | 
|  |  | 
|  | - A user thread will never by default have access to user thread stacks | 
|  | that are not members of the same memory domain. | 
|  |  | 
|  | - A user thread will never by default have access to thread stacks owned | 
|  | by a supervisor thread, or thread stacks used to handle system call | 
|  | privilege elevations, interrupts, or CPU exceptions. | 
|  |  | 
|  | - A user thread may have read/write access to the stacks of other user | 
|  | threads in the same memory domain, depending on hardware. | 
|  |  | 
|  | - On MPU systems, threads may only access their own stack buffer. | 
|  |  | 
|  | - On MMU systems, threads may access any user thread stack in the same | 
|  | memory domain. Portable code should not assume this. | 
|  |  | 
|  | - By default, program text and read-only data are accessible to all threads | 
|  | on read-only basis, kernel-wide. This policy may be adjusted. | 
|  |  | 
|  | - User threads by default are not granted default access to any memory | 
|  | except what is noted above. | 
|  |  | 
|  | - We prevent use of device drivers or kernel objects not specifically granted, | 
|  | with the permission granularity on a per object or per driver instance | 
|  | basis. | 
|  |  | 
|  | - We validate kernel or driver API calls with incorrect parameters that would | 
|  | otherwise cause a crash or corruption of data structures private to the | 
|  | kernel. This includes: | 
|  |  | 
|  | - Using the wrong kernel object type. | 
|  |  | 
|  | - Using parameters outside of proper bounds or with nonsensical values. | 
|  |  | 
|  | - Passing memory buffers that the calling thread does not have sufficient | 
|  | access to read or write, depending on the semantics of the API. | 
|  |  | 
|  | - Use of kernel objects that are not in a proper initialization state. | 
|  |  | 
|  | - We ensure the detection and safe handling of user mode stack overflows. | 
|  |  | 
|  | - We prevent invoking system calls to functions excluded by the kernel | 
|  | configuration. | 
|  |  | 
|  | - We prevent disabling of or tampering with kernel-defined and hardware- | 
|  | enforced memory protections. | 
|  |  | 
|  | - We prevent re-entry from user to supervisor mode except through the kernel- | 
|  | defined system calls and interrupt handlers. | 
|  |  | 
|  | - We prevent the introduction of new executable code by user mode threads, | 
|  | except to the extent to which this is supported by kernel system calls. | 
|  |  | 
|  | We are specifically not protecting against the following attacks: | 
|  |  | 
|  | - The kernel itself, and any threads that are executing in supervisor mode, | 
|  | are assumed to be trusted. | 
|  |  | 
|  | - The toolchain and any supplemental programs used by the build system are | 
|  | assumed to be trusted. | 
|  |  | 
|  | - The kernel build is assumed to be trusted. There is considerable build-time | 
|  | logic for creating the tables of valid kernel objects, defining system calls, | 
|  | and configuring interrupts. The .elf binary files that are worked with | 
|  | during this process are all assumed to be trusted code. | 
|  |  | 
|  | - We can't protect against mistakes made in memory domain configuration done in | 
|  | kernel mode that exposes private kernel data structures to a user thread. RAM | 
|  | for kernel objects should always be configured as supervisor-only. | 
|  |  | 
|  | - It is possible to make top-level declarations of user mode threads and | 
|  | assign them permissions to kernel objects. In general, all C and header | 
|  | files that are part of the kernel build producing zephyr.elf are assumed to | 
|  | be trusted. | 
|  |  | 
|  | - We do not protect against denial of service attacks through thread CPU | 
|  | starvation. Zephyr has no thread priority aging and a user thread of a | 
|  | particular priority can starve all threads of lower priority, and also other | 
|  | threads of the same priority if time-slicing is not enabled. | 
|  |  | 
|  | - There are build-time defined limits on how many threads can be active | 
|  | simultaneously, after which creation of new user threads will fail. | 
|  |  | 
|  | - Stack overflows for threads running in supervisor mode may be caught, | 
|  | but the integrity of the system cannot be guaranteed. | 
|  |  | 
|  | High-level Policy Details | 
|  | ************************* | 
|  |  | 
|  | Broadly speaking, we accomplish these thread-level memory protection goals | 
|  | through the following mechanisms: | 
|  |  | 
|  | - Any user thread will only have access to a subset of memory: | 
|  | typically its stack, program text, read-only data, and any partitions | 
|  | configured in the :ref:`memory_domain` it belongs to. Access to any other RAM | 
|  | must be done on the thread's behalf through system calls, or specifically | 
|  | granted by a supervisor thread using the memory domain APIs. Newly created | 
|  | threads inherit the memory domain configuration of the parent. Threads may | 
|  | communicate with each other by having shared membership of the same memory | 
|  | domains, or via kernel objects such as semaphores and pipes. | 
|  |  | 
|  | - User threads cannot directly access memory belonging to kernel objects. | 
|  | Although pointers to kernel objects are used to reference them, actual | 
|  | manipulation of kernel objects is done through system call interfaces. Device | 
|  | drivers and threads stacks are also considered kernel objects. This ensures | 
|  | that any data inside a kernel object that is private to the kernel cannot be | 
|  | tampered with. | 
|  |  | 
|  | - User threads by default have no permission to access any kernel object or | 
|  | driver other than their own thread object. Such access must be granted by | 
|  | another thread that is either in supervisor mode or has permission on both | 
|  | the receiving thread object and the kernel object being granted access to. | 
|  | The creation of new threads has an option to automatically inherit | 
|  | permissions of all kernel objects granted to the parent, except the parent | 
|  | thread itself. | 
|  |  | 
|  | - For performance and footprint reasons Zephyr normally does little or no | 
|  | parameter error checking for kernel object or device driver APIs. Access from | 
|  | user mode through system calls involves an extra layer of handler functions, | 
|  | which are expected to rigorously validate access permissions and type of | 
|  | the object, check the validity of other parameters through bounds checking or | 
|  | other means, and verify proper read/write access to any memory buffers | 
|  | involved. | 
|  |  | 
|  | - Thread stacks are defined in such a way that exceeding the specified stack | 
|  | space will generate a hardware fault. The way this is done specifically | 
|  | varies per architecture. | 
|  |  | 
|  | Constraints | 
|  | *********** | 
|  |  | 
|  | All kernel objects, thread stacks, and device driver instances must be defined | 
|  | at build time if they are to be used from user mode. Dynamic use-cases for | 
|  | kernel objects will need to go through pre-defined pools of available objects. | 
|  |  | 
|  | There are some constraints if additional application binary data is loaded | 
|  | for execution after the kernel starts: | 
|  |  | 
|  | - Loaded object code will not be able to define any kernel objects that will be | 
|  | recognized by the kernel. This code will instead need to use APIs for | 
|  | requesting kernel objects from pools. | 
|  |  | 
|  | - Similarly, since the loaded object code will not be part of the kernel build | 
|  | process, this code will not be able to install interrupt handlers, | 
|  | instantiate device drivers, or define system calls, regardless of what | 
|  | mode it runs in. | 
|  |  | 
|  | - Loaded object code that does not come from a verified source should always | 
|  | be entered with the CPU already in user mode. |