| .. SPDX-License-Identifier: GPL-2.0 |
| |
| =============================== |
| Software Guard eXtensions (SGX) |
| =============================== |
| |
| Overview |
| ======== |
| |
| Software Guard eXtensions (SGX) hardware enables for user space applications |
| to set aside private memory regions of code and data: |
| |
| * Privileged (ring-0) ENCLS functions orchestrate the construction of the |
| regions. |
| * Unprivileged (ring-3) ENCLU functions allow an application to enter and |
| execute inside the regions. |
| |
| These memory regions are called enclaves. An enclave can be only entered at a |
| fixed set of entry points. Each entry point can hold a single hardware thread |
| at a time. While the enclave is loaded from a regular binary file by using |
| ENCLS functions, only the threads inside the enclave can access its memory. The |
| region is denied from outside access by the CPU, and encrypted before it leaves |
| from LLC. |
| |
| The support can be determined by |
| |
| ``grep sgx /proc/cpuinfo`` |
| |
| SGX must both be supported in the processor and enabled by the BIOS. If SGX |
| appears to be unsupported on a system which has hardware support, ensure |
| support is enabled in the BIOS. If a BIOS presents a choice between "Enabled" |
| and "Software Enabled" modes for SGX, choose "Enabled". |
| |
| Enclave Page Cache |
| ================== |
| |
| SGX utilizes an *Enclave Page Cache (EPC)* to store pages that are associated |
| with an enclave. It is contained in a BIOS-reserved region of physical memory. |
| Unlike pages used for regular memory, pages can only be accessed from outside of |
| the enclave during enclave construction with special, limited SGX instructions. |
| |
| Only a CPU executing inside an enclave can directly access enclave memory. |
| However, a CPU executing inside an enclave may access normal memory outside the |
| enclave. |
| |
| The kernel manages enclave memory similar to how it treats device memory. |
| |
| Enclave Page Types |
| ------------------ |
| |
| **SGX Enclave Control Structure (SECS)** |
| Enclave's address range, attributes and other global data are defined |
| by this structure. |
| |
| **Regular (REG)** |
| Regular EPC pages contain the code and data of an enclave. |
| |
| **Thread Control Structure (TCS)** |
| Thread Control Structure pages define the entry points to an enclave and |
| track the execution state of an enclave thread. |
| |
| **Version Array (VA)** |
| Version Array pages contain 512 slots, each of which can contain a version |
| number for a page evicted from the EPC. |
| |
| Enclave Page Cache Map |
| ---------------------- |
| |
| The processor tracks EPC pages in a hardware metadata structure called the |
| *Enclave Page Cache Map (EPCM)*. The EPCM contains an entry for each EPC page |
| which describes the owning enclave, access rights and page type among the other |
| things. |
| |
| EPCM permissions are separate from the normal page tables. This prevents the |
| kernel from, for instance, allowing writes to data which an enclave wishes to |
| remain read-only. EPCM permissions may only impose additional restrictions on |
| top of normal x86 page permissions. |
| |
| For all intents and purposes, the SGX architecture allows the processor to |
| invalidate all EPCM entries at will. This requires that software be prepared to |
| handle an EPCM fault at any time. In practice, this can happen on events like |
| power transitions when the ephemeral key that encrypts enclave memory is lost. |
| |
| Application interface |
| ===================== |
| |
| Enclave build functions |
| ----------------------- |
| |
| In addition to the traditional compiler and linker build process, SGX has a |
| separate enclave “build” process. Enclaves must be built before they can be |
| executed (entered). The first step in building an enclave is opening the |
| **/dev/sgx_enclave** device. Since enclave memory is protected from direct |
| access, special privileged instructions are then used to copy data into enclave |
| pages and establish enclave page permissions. |
| |
| .. kernel-doc:: arch/x86/kernel/cpu/sgx/ioctl.c |
| :functions: sgx_ioc_enclave_create |
| sgx_ioc_enclave_add_pages |
| sgx_ioc_enclave_init |
| sgx_ioc_enclave_provision |
| |
| Enclave runtime management |
| -------------------------- |
| |
| Systems supporting SGX2 additionally support changes to initialized |
| enclaves: modifying enclave page permissions and type, and dynamically |
| adding and removing of enclave pages. When an enclave accesses an address |
| within its address range that does not have a backing page then a new |
| regular page will be dynamically added to the enclave. The enclave is |
| still required to run EACCEPT on the new page before it can be used. |
| |
| .. kernel-doc:: arch/x86/kernel/cpu/sgx/ioctl.c |
| :functions: sgx_ioc_enclave_restrict_permissions |
| sgx_ioc_enclave_modify_types |
| sgx_ioc_enclave_remove_pages |
| |
| Enclave vDSO |
| ------------ |
| |
| Entering an enclave can only be done through SGX-specific EENTER and ERESUME |
| functions, and is a non-trivial process. Because of the complexity of |
| transitioning to and from an enclave, enclaves typically utilize a library to |
| handle the actual transitions. This is roughly analogous to how glibc |
| implementations are used by most applications to wrap system calls. |
| |
| Another crucial characteristic of enclaves is that they can generate exceptions |
| as part of their normal operation that need to be handled in the enclave or are |
| unique to SGX. |
| |
| Instead of the traditional signal mechanism to handle these exceptions, SGX |
| can leverage special exception fixup provided by the vDSO. The kernel-provided |
| vDSO function wraps low-level transitions to/from the enclave like EENTER and |
| ERESUME. The vDSO function intercepts exceptions that would otherwise generate |
| a signal and return the fault information directly to its caller. This avoids |
| the need to juggle signal handlers. |
| |
| .. kernel-doc:: arch/x86/include/uapi/asm/sgx.h |
| :functions: vdso_sgx_enter_enclave_t |
| |
| ksgxd |
| ===== |
| |
| SGX support includes a kernel thread called *ksgxd*. |
| |
| EPC sanitization |
| ---------------- |
| |
| ksgxd is started when SGX initializes. Enclave memory is typically ready |
| for use when the processor powers on or resets. However, if SGX has been in |
| use since the reset, enclave pages may be in an inconsistent state. This might |
| occur after a crash and kexec() cycle, for instance. At boot, ksgxd |
| reinitializes all enclave pages so that they can be allocated and re-used. |
| |
| The sanitization is done by going through EPC address space and applying the |
| EREMOVE function to each physical page. Some enclave pages like SECS pages have |
| hardware dependencies on other pages which prevents EREMOVE from functioning. |
| Executing two EREMOVE passes removes the dependencies. |
| |
| Page reclaimer |
| -------------- |
| |
| Similar to the core kswapd, ksgxd, is responsible for managing the |
| overcommitment of enclave memory. If the system runs out of enclave memory, |
| *ksgxd* “swaps” enclave memory to normal memory. |
| |
| Launch Control |
| ============== |
| |
| SGX provides a launch control mechanism. After all enclave pages have been |
| copied, kernel executes EINIT function, which initializes the enclave. Only after |
| this the CPU can execute inside the enclave. |
| |
| EINIT function takes an RSA-3072 signature of the enclave measurement. The function |
| checks that the measurement is correct and signature is signed with the key |
| hashed to the four **IA32_SGXLEPUBKEYHASH{0, 1, 2, 3}** MSRs representing the |
| SHA256 of a public key. |
| |
| Those MSRs can be configured by the BIOS to be either readable or writable. |
| Linux supports only writable configuration in order to give full control to the |
| kernel on launch control policy. Before calling EINIT function, the driver sets |
| the MSRs to match the enclave's signing key. |
| |
| Encryption engines |
| ================== |
| |
| In order to conceal the enclave data while it is out of the CPU package, the |
| memory controller has an encryption engine to transparently encrypt and decrypt |
| enclave memory. |
| |
| In CPUs prior to Ice Lake, the Memory Encryption Engine (MEE) is used to |
| encrypt pages leaving the CPU caches. MEE uses a n-ary Merkle tree with root in |
| SRAM to maintain integrity of the encrypted data. This provides integrity and |
| anti-replay protection but does not scale to large memory sizes because the time |
| required to update the Merkle tree grows logarithmically in relation to the |
| memory size. |
| |
| CPUs starting from Icelake use Total Memory Encryption (TME) in the place of |
| MEE. TME-based SGX implementations do not have an integrity Merkle tree, which |
| means integrity and replay-attacks are not mitigated. B, it includes |
| additional changes to prevent cipher text from being returned and SW memory |
| aliases from being created. |
| |
| DMA to enclave memory is blocked by range registers on both MEE and TME systems |
| (SDM section 41.10). |
| |
| Usage Models |
| ============ |
| |
| Shared Library |
| -------------- |
| |
| Sensitive data and the code that acts on it is partitioned from the application |
| into a separate library. The library is then linked as a DSO which can be loaded |
| into an enclave. The application can then make individual function calls into |
| the enclave through special SGX instructions. A run-time within the enclave is |
| configured to marshal function parameters into and out of the enclave and to |
| call the correct library function. |
| |
| Application Container |
| --------------------- |
| |
| An application may be loaded into a container enclave which is specially |
| configured with a library OS and run-time which permits the application to run. |
| The enclave run-time and library OS work together to execute the application |
| when a thread enters the enclave. |
| |
| Impact of Potential Kernel SGX Bugs |
| =================================== |
| |
| EPC leaks |
| --------- |
| |
| When EPC page leaks happen, a WARNING like this is shown in dmesg: |
| |
| "EREMOVE returned ... and an EPC page was leaked. SGX may become unusable..." |
| |
| This is effectively a kernel use-after-free of an EPC page, and due |
| to the way SGX works, the bug is detected at freeing. Rather than |
| adding the page back to the pool of available EPC pages, the kernel |
| intentionally leaks the page to avoid additional errors in the future. |
| |
| When this happens, the kernel will likely soon leak more EPC pages, and |
| SGX will likely become unusable because the memory available to SGX is |
| limited. However, while this may be fatal to SGX, the rest of the kernel |
| is unlikely to be impacted and should continue to work. |
| |
| As a result, when this happens, user should stop running any new |
| SGX workloads, (or just any new workloads), and migrate all valuable |
| workloads. Although a machine reboot can recover all EPC memory, the bug |
| should be reported to Linux developers. |
| |
| |
| Virtual EPC |
| =========== |
| |
| The implementation has also a virtual EPC driver to support SGX enclaves |
| in guests. Unlike the SGX driver, an EPC page allocated by the virtual |
| EPC driver doesn't have a specific enclave associated with it. This is |
| because KVM doesn't track how a guest uses EPC pages. |
| |
| As a result, the SGX core page reclaimer doesn't support reclaiming EPC |
| pages allocated to KVM guests through the virtual EPC driver. If the |
| user wants to deploy SGX applications both on the host and in guests |
| on the same machine, the user should reserve enough EPC (by taking out |
| total virtual EPC size of all SGX VMs from the physical EPC size) for |
| host SGX applications so they can run with acceptable performance. |
| |
| Architectural behavior is to restore all EPC pages to an uninitialized |
| state also after a guest reboot. Because this state can be reached only |
| through the privileged ``ENCLS[EREMOVE]`` instruction, ``/dev/sgx_vepc`` |
| provides the ``SGX_IOC_VEPC_REMOVE_ALL`` ioctl to execute the instruction |
| on all pages in the virtual EPC. |
| |
| ``EREMOVE`` can fail for three reasons. Userspace must pay attention |
| to expected failures and handle them as follows: |
| |
| 1. Page removal will always fail when any thread is running in the |
| enclave to which the page belongs. In this case the ioctl will |
| return ``EBUSY`` independent of whether it has successfully removed |
| some pages; userspace can avoid these failures by preventing execution |
| of any vcpu which maps the virtual EPC. |
| |
| 2. Page removal will cause a general protection fault if two calls to |
| ``EREMOVE`` happen concurrently for pages that refer to the same |
| "SECS" metadata pages. This can happen if there are concurrent |
| invocations to ``SGX_IOC_VEPC_REMOVE_ALL``, or if a ``/dev/sgx_vepc`` |
| file descriptor in the guest is closed at the same time as |
| ``SGX_IOC_VEPC_REMOVE_ALL``; it will also be reported as ``EBUSY``. |
| This can be avoided in userspace by serializing calls to the ioctl() |
| and to close(), but in general it should not be a problem. |
| |
| 3. Finally, page removal will fail for SECS metadata pages which still |
| have child pages. Child pages can be removed by executing |
| ``SGX_IOC_VEPC_REMOVE_ALL`` on all ``/dev/sgx_vepc`` file descriptors |
| mapped into the guest. This means that the ioctl() must be called |
| twice: an initial set of calls to remove child pages and a subsequent |
| set of calls to remove SECS pages. The second set of calls is only |
| required for those mappings that returned a nonzero value from the |
| first call. It indicates a bug in the kernel or the userspace client |
| if any of the second round of ``SGX_IOC_VEPC_REMOVE_ALL`` calls has |
| a return code other than 0. |