Complets: Universal Compartmentalisation and Programming Model For Arm Permission Overlay Extension 2

Vasily A. Sartakov
Huawei R&D


Abstract

Arm Permission Overlay Extension (POE) is an intra‑process isolation mechanism based on memory protection keys. This mechanism partitions virtual memory into regions whose access permissions can be reconfigured without invoking higher exception levels. Because POE does not enforce privilege separation, POE‑based protection domains are applicable only as a software fault‑isolation technique.

The POE2 is an isolation mechanism that substantially extends POE1. Dealing with memory protection keys, it also introduces new registers and tables that regulate the operations permitted for code executing within protection domains associated with different keys. A permission is determined by the spatial index of the executing code (i.e., the protection key associated with code), the spatial index of the accessed memory, the operation type, and a new attribute of the thread context: its temporal index.

POE2 provides stronger security guarantees than POE1, but also introduces considerable architectural complexity. Effective permissions arise from the interaction of multiple hardware components, making direct programming non-trivial and prone to subtle errors. In the paper, we present a detailed analysis of POE2 and introduce a universal programming model with a strong security model for POE2‑based systems. The model abstracts the complexity of spatial and temporal indices while enabling typical patterns of partitioned software constructed using intra‑process isolation.

1 Introduction↩︎

Virtual memory and processes are fundamental mechanisms that enable isolated program execution. Each program executes within its own address space, without visibility into how virtual addresses are backed by hardware or whether other programs exist. If virtual address spaces are isolated and no pages are shared, programs cannot directly influence one another and may communicate only through the privileged intermediary – the kernel.

The kernel serves as this privileged intermediary and provides mechanisms to configure the mmu. Interaction between processes requires transitions into privileged mode, which impose performance overhead on communicating programs. Consequently, the trade-off between the size of the tcb and communication performance is driven by practical considerations: secure deployments favour finer‑grained isolation with higher communication costs, whereas less secure deployments permit more monolithic software with lower overhead.

One approach to reducing the overhead associated with process‑based isolation – commonly referred to as the mmu tax – is to employ intra‑process isolation mechanisms and construct communication primitives that avoid privileged transitions. Over the last decade, several techniques have been proposed: Intel mpk [1] and Arm poe [2] allow assigning protection keys to memory pages and modifying page attributes without kernel involvement. Once keys are assigned, the effective permissions of associated pages are determined by the key’s permissions, and updates to key attributes do not require kernel mediation. Using these extensions, prior work has demonstrated fine‑grained software partitioning with low performance overhead [3].

A key limitation of Arm poe and Intel mpk is the absence of privilege separation within compartments – software components isolated using protection keys. With mpk, compartmentalised code can modify access rights of other compartments via the wrpkru instruction [4]. On Arm, compartmentalised code can similarly modify the POR_ELx register. In both cases, these operations occur without kernel oversight, enabling efficient cross‑compartment communication but preventing the use of these mechanisms to isolate adversarial code. Despite attempts to reduce the likelihood of executing security‑sensitive instructions [5][7], protection‑key‑based compartment isolation has been treated primarily as software‑fault isolation.

Figure 1: Communication between isolated parties: 1⃝ IPC between processes using kernel; 2⃝ Cross-compartment calls without the kernel

Arm addressed the security limitations of poe1 and announced [8] a new version, poe2. poe2 introduces privilege separation for compartmentalised code. As in poe1, the address space can be partitioned into protection domains by assigning keys, but permissions are now determined by a combination of indices associated with the executing code’s location, the thread state, the operation type, and the target memory.

Programming poe2 requires kernel involvement, but – as with poe and mpk – transitions between isolated components can occur without kernel mediation. However, the permissions governing operations are defined in tables managed by the operating system. Arm does not provide an explicit programming model for poe2, and in this work we present a programming model that abstracts the complexity of new instructions, tables, and states while offering universal primitives for implementing typical intra‑process isolation patterns.

The paper makes the following contribution: It

  1. provides an overview of poe2 for el0, including key mechanisms, new instructions, registers, and tables

  2. analyses the security properties of poe2 and shows that secure transitions between isolated components require trusted elements

  3. introduces a universal programming model that implements key intra‑process design patterns, including asymmetrical (jit isolation), symmetrical (mutually distrusted parties), and enclave‑style reversed-sandbox.

The rest of the paper is organised as follows: We begin with background on poe2 and its security analysis in Section 2. In Section 3, we introduce the poe2 programming model, which abstracts its complexity and enables the implementation of typical design patterns, considered later in Section 4.

2 Background↩︎

poe2 introduces a set of new instructions, registers, and in‑memory tables. It builds on prior extensions, including poe1 and the Permission Indirection Extension (PIE). Different exception levels may employ different mechanisms for transitions between software components: for exception level 0 (user space) a single approach is available, and this work focuses on that case.

poe1 comprised two principal elements: POIndexes (keys) stored in page table entries, and the POR_ELx registers, whose bit fields specify permissions applied to memory associated with a given key. poe2 reuses and extends these mechanisms: POIndexes remain encoded in page table entries, and poe1 may be used together with, or independently of, poe2. poe permissions are no held in a single register, instead they reside in memory tables such as irt, ttt, dpot, and others.

Permissions are no longer determined solely by the spatial characteristic of the CPU context, i.e. the key associated with the currently executing code or the accessed memory. A new register, TINDEX, introduces a temporal characteristic of a CPU context, providing an orthogonal dimension for identifying contexts: a thread may switch from one state to another by changing this index, and the permitted operations are defined by the combination of the spatial and temporal characteristics of the context, the operation type (fetch, read, write), and the spatial characteristics of the target memory [9]. In other words, for non‑poe2 instructions (for example load, store, fetch), the combination of spatial and temporal indexes defines the current unique index of the executing thread, and the effective permissions applied to the current instruction are determined by the combination of this index and the spatial index of the target memory.

a
b

Figure 2: Two isolated yet components and the corresponding poe2 configuration: Component C#1 with POIndex=1 can write into shared memory shm with POIndex=3 and switch to Component C#2 (POIndex=2) with read-only access to shared memory, performing TCHANGE and BR instructions. a — Two compartments with shared buffer and transition, b — Key tables defining permissions: IRT, DPOT, TTT

Figure 2 illustrates a simple scenario with two interacting software components, and the corresponding poe2 table configuration. Isolated component C#1 has write access to the shared memory shm, while isolated component C#2 has read access. Both C#1 and C#2 are mutually distrusted and lack access to data outside their own compartments and the shared buffer; however, C#1 is permitted to perform a domain transition to C#2.

The corresponding poe2 table configuration is shown in Figure 2 (b). The irt is indexed by POIndex (the key in the page tables entries associated with a page) and by TIndex – the new register in the CPU context. Each irt entry combines two state indexes, POTIndex and FGDTIndex (not shown), with an execution flag that enables fetch in that state [9]. POTIndex indexes the dpot and the ttt, while FGDTIndex is used for fine‑grained dynamic traps.

To enable the scenario in Figure 2 (a), the irt must include three POTIndexes – one for each party involved. POTIndex=0 is used for the state TIndex=0 and POIndex=1. This POTIndex corresponds to ordinary execution inside the compartment created with key 1. Fetch is enabled for this index combination.

POTIndex=1 is used for the state TIndex=1 and POIndex=2 and defines execution of the second component. The shared memory is not executable and therefore lacks the X bit, and it has no irt entry for execution. A transition state that enables switching between isolated binaries is represented by POTIndex=2, which is defined for TIndex=1 and POIndex=1. This index represents an execution state of the isolated code with permissions distinct from ordinary execution states.

Isolated contexts in C#1 and C#2 possess distinct TIndex and POIndex values, which prevents direct code transitions between them. Indeed, in a single step, only one index may change: a temporal index effected by the TCHANGE instruction, or a spatial index effected by a branch instruction (which transfers control to memory associated with a different POIndex). The temporal and spatial indices are represented and modified independently, and there is no mechanism to update both indices atomically. Therefore, a single‑step transition that alters both indices is impossible. POE2 nevertheless permits controlled transitions by composing these single‑index operations: a transition proceeds via an intermediate state in which one index retains its previous value, so the overall change is realised as a sequence of TCHANGE and branch operations.

POTIndex=2 describes the intermediate state in which code from C#1 is spatially associated with C#1 but has a TIndex equal to C#2, enabling a branch into C#2’s memory. Changes to TIndex are governed by the ttt, which is indexed by POTIndex and the target TIndex. Consequently, isolated code may elevate its temporal index only to values for which transitions are explicitly enabled. Branches are not similarly constrained as they are generally permitted between POIndexes and the same TIndex but can be restricted by using different TIndex values. As a result, C#1 and C#2 have unique TIndex–POIndex pairs.

The ttt, which controls transitions betweens TIndexes, contains two records: one for POTIndex=0 (with TIndex=0) permitting transition into TIndex=1 so that a branch into C#2 becomes possible, and one for POTIndex=2 (with TIndex=1) permitting transition back into TIndex=0. Forward and return transitions may be implemented in different orders: either TCHANGE then branch, or branch then TCHANGE. The actual sequence significantly affects the security and programming models, which effects we consider later.

To access the shared buffer shm and to permit execution of isolated code, the dpot contains several records. First, both C#1 and C#2 have their own records with read and write attributes. POTIndex=0 with POIndex=1 enables RW access to data owned by C#1 for C#1 code. POTIndex=1 with POIndex=2 enables analogous access for C#2. Access to the shared buffer is enabled via two separate dpot records, each associated with the default POTIndex of the respective isolated components and with a dedicated POIndex=3 for the buffer: C#1 has write access with POTIndex=0 and POIndex=3, while C#2 has read access with POTIndex=1 and POIndex=3.

poe2 is a complex extension comprising multiple tables, registers, and instructions; not all elements are considered in this example. The scenario presented captures a canonical design pattern for isolated components: transitions between mutually distrusted parties and data exchange via a shared buffer.

2.1 Challenges↩︎

As the example shows, configuring poe2 components is a complex task that is prone to error. An incorrectly chosen index can grant unintended permissions or enable unintended transitions. Below we summarise the key challenges of programming poe2 and the motivation for a unified programming model.

2.1.0.1 Lack of Semantics

poe2 operates in a multi‑dimensional space of indices, where a particular permission is defined by a POTIndex defined by the base indices (POIndex and TIndex), the operation performed (read/write/TCHANGE/fetch), and the base indices of the target memory or state. poe1, Intel MPK, and other intra‑process isolation approaches, use a spatial index (i.e. a key) to denote a compartment – protection domain for execution units.

In poe2, the spatial index alone does not define an execution unit because the permission model is constructed from combinations of different indices. poe2 distinguishes permissions for code executing in memory with the same POIndex but different TIndexes, and it controls transitions between these states via the ttt [9].

At the same time, permissions also change when the spatial index changes, and spatial changes are not explicitly controlled but are implicit: within a single TIndex nothing prevents branching between code located in different POIndexes (if pages are executable). To control branching, a programmer must therefore assign different TIndexes. In other words, poe2 lacks a clear semantic for controlling transitions between states: it provides explicit control over only one of the two orthogonal characteristics.

2.1.0.2 Transition Risks

The kernel’s asymmetric permission model for user–kernel interaction relies on system calls and trap‑based domain transitions. When a user‑space thread issues a system call, it cannot influence how the kernel dispatches that call: execution traps to a well‑defined handler and user code has no mechanism to transfer control to arbitrary kernel addresses.

In contrast, in poe2 operating at EL02, a caller may branch to any executable region of a callee’s memory if the branch is permitted (for example when both caller and callee share the same TIndex). Conceptually, this implies that two mutually distrusted parties that communicate in an RPC‑like fashion cannot fully control the control‑flow of the interaction: the counterparty may transfer control to an arbitrary address within the permitted region.

The risk applies equally to interactions between trusted code and an untrusted sandbox. A transition into the sandbox can be arranged by the trusted component, but control of the return or subsequent branches originating inside the sandbox is not constrained by the trusted component. A sandboxed jit can branch into code sequences within the trusted region that the trusted component did not intend to expose. Control‑flow hardening primitives such as BTI or authenticated pointers do not eliminate this risk because they do not compel untrusted code to use specific trampolines or to restrict its branch targets to the addresses supplied by the trusted component.

These transition risks motivate a programming model that abstracts the multi‑index configuration and enforces controlled transition and permission semantics.

3 Programming Model↩︎

From the software perspective, poe2 assumes a two‑phase life‑cycle for isolated software. A privileged entity is responsible for deploying software components into distinct memory regions. It then configures the poe2 tables by assigning permissions to states defined by POIndexes and TIndexes. In the second phase, the partitioned software executes and performs domain transitions without kernel involvement.

We develop our programming model to follow this system design. The monitor is a privileged component within a process responsible for the life‑cycle of so‑called Complets: a poe2‑tailored abstraction for isolated binaries. The Monitor deploys and links binaries to enable their communication. It exposes a hostcall interface to Complets and interacts with the kernel to configure poe2 tables or to obtain mechanisms unavailable in user space. Conceptually, the model does not prevent Complets from interacting with the kernel, but the performance benefits of poe2 derive from enabling intra‑process communication without kernel involvement.

3.0.0.1 Security assumptions

By default, we assume compartmentalised code is adversarial. It may execute any instruction; we assume it is hostile and aims to escape its compartment. Management software is trusted: its security‑critical components (in particular, trampolines) are assumed implemented correctly and free of bugs, while the remainder of a binary may contain ROP gadgets or other payloads that compartmentalised code may attempt to exploit. The kernel is trusted and poe2 is assumed to behave according to its specification. Less strict security models can be derived and implemented using the programming model presented here.

Table 1: API to use complets by Monitor: create complets for code and trampoline, bridge complet with monitor via trampoline
API Functions Purpose / Details
mon = init_default_complet() Initialise POE2 tables for Monitor code
c1 = deploy_complet(path, base_addr, size) Allocate memory, load elf inside, setup POE2 tables
t1 = deploy_complet("trampoline", base_addr, size)
enable_transition(c1, t1) Update POE2 tables to enable transitions from c1 into t1
enable_transition(t1, mon)
install_hook(c1, "write", t1) Replace "write" function with trampoline provided by t1
c1_ctx,mon_ctx = alloc_shared(size) Allocate memory to store thread contexts
enable_shared(c1_ctx, t1, rw) Grant read-write access to shared memory for context

3.0.0.2 Complet

We introduce a system abstraction, the complet — a compartmentalised applet. Complets are mutually distrusted, cannot access each other’s memory, and interact only via shared memory. Each complet defines a protection domain for its threads; threads perform transitions between complets in a trusted manner to realise inter‑complet communication.

Under the default, strict security model, complets are mapped to poe2 structures as follows:

  • no two complets share the same TIndex or POIndex. Consequently, allocating a new complet requires assigning fresh POIndex and TIndex values

  • Shared memory regions receive distinct POIndexes. Each complet obtains its own access rights to shared regions via a POTIndex derived from its POIndex and TIndex.

3.1 Trampolines↩︎

Threads within complets cannot branch into addresses belonging to other complets, yet they must exchange information. Domain transitions between complets are provided by the compartmentalisation runtime as trusted trampolines. The programming model is centred on the privileged Monitor, which mediates and bridges isolated components according to a predefined partitioning.

The partitioning may be encoded in the ELF image or established by the Monitor at deployment; in either case the model assumes that isolated code is poe2‑protected and isolation‑unaware, and therefore does not advertise public interfaces or interact with the Monitor autonomously. The Monitor performs poe2 configuration, exposes a hostcall interface to complets installing hooks, and interacts with the kernel on their behalf. This design preserves the performance advantages of poe2 by enabling intra‑process communication without routine kernel mediation.

4 Implementation↩︎

Table 1 shows API functions implementing basic usage of complets. The code initialises three complets: compartmentalised code with binary pointed by *path, complet with trusted code of trampoline, and initialises the default complet for Monitor code. It than enables transitions between complets updating the dpot and ttt for the corresponding indexes stored inside complet objects. Subsequently, install_hook() patches the code inside complet to route the write() function into trusted trampoline. Finally, the code allocates memory to store component contexts and grants permissions to these contexts.

Figure 3 shows the secure transition enforced by the programming model. The transition begins inside a complet: the hook installed by the Monitor inserts a branch instruction that transfers control into the memory region of TRAMP. In this state, the compartmentalised code may perform only two operations: execute its own code (i.e. execute in [POIndex=2, TIndex=2]), or branch into a trampoline’s POIndex. The trampoline code is minimal and stateless. This design prevents payload extraction and relies on a small code size to reduce the attack surface; the trampoline is below 100 assembler instructions. While in the [POIndex=1,TIndex=2] state, execution is restricted to either issuing a TChange instruction with transition into the [POIndex=1,TIndex=1] state, or performing a jump that returns control to C1. The code may be further constrained using BTI instructions to enforce landing at the trampoline entry. After issuing TChange, the trampoline obtains the TIndex of the originating complet, which can subsequently be used to identify the caller.

The core trampoline implements a context switch from a complet’s context to the Monitor context using a memory buffers allocated by the Monitor. These buffers are writable only when the trampoline is in the [POIndex=1,TIndex=1] state. The trampoline then escalates its TIndex to the Monitor’s value and branches into the Monitor code responsible for handling the hook for the write function.

4.1 Evaluation of Key Design Patterns↩︎

We implement a prototype of the compartmentalisation system, the Monitor, and the trusted trampoline. The full one-way transition requires approximately 75 instructions to store and load contexts and to perform domain transitions. We add support for poe2 in the Linux kernel by extending the existing mpk API with new syscalls; this change comprises roughly 300 new source lines of code for poe2 table management. Using the compartmentalisation library, we prototype representative design patterns for intra-process isolation and evaluate them on a POE2-enabled functional model (Arm’s FVP [10]).

4.1.0.1 Symmetric

The symmetric isolation scheme assumes mutually distrusted parties that cannot directly access each other and require a third party for data exchange or transitions. Prior systems that follow this pattern include Hodor [6], IskiOS [11], libmpk [12], SOAAP [13], LwCs [14], SeCage [15], CubicleOS [16], FlexOS [17], and others. The complet’s API and programming model adopt this pattern by default; therefore, systems that use symmetric partitioning can be reproduced with complets and poe2 with minimal adaptation.

4.1.0.2 Asymmetric

The asymmetric isolation scheme for jited code separates code into two parts: an untrusted region for generated code and a trusted one. The untrusted region is constrained to its own memory, while the trusted region has access to both its own memory and the untrusted region’s memory. This arrangement enables efficient data exchange because the trusted code can access data directly without copying between protection domains. Systems that employ this pattern include ERIM [7], Shreds [18], CETIS [19], E-IM [20], PrivTrans [21], Wedge [22], and others.

The Complets API supports this scenario: the trusted component (Monitor) can request read/write access to an untrusted complet in the same manner as requesting access to shared memory. Because the Monitor’s and the untrusted complet’s key indexes are known, the asymmetric model is straightforward to implement by enabling read/write entries in the dpot for the Monitor’s POTIndex.

4.1.0.3 Enclave

Secure enclaves, as introduced by Intel SGX [23], represent an asymmetric design in which an intra-process isolated component has full access to the host process (except other enclaves), while non-enclaved software has no access to enclaves. Enclave deployment and manipulation use special instructions and are not controlled by the mmu in the same way as mpk or poe. Despite differences in lifecycle and security assumptions relative to poe2, the enclave model for data access can be reproduced on top of poe2 and the Complets API. Specifically, the Monitor can restrict its read–write access to designated “enclave” complet and enable read–write access to its own memory for those complets.

Figure 3: Transition from complet to Monitor via trusted trampolines: preserves ID and guarantees secure landing

5 Discussion↩︎

5.0.0.1 Weaker Security

The Complets API supports a strong security model in which isolated software is treated as hostile. To ensure transitions from isolated code land at intended Monitor functions, we use an intermediate trusted trampoline that is stateless and has a small footprint. This approach enforces trusted transitions but requires additional POIndex/TIndex values and the use of TChange instructions, which may incur performance overhead. A weaker programming model is possible: if the Monitor code is assumed bug-free and incapable of containing payload, isolated code may transfer control directly into Monitor code. The total number of registers stored and restored during the transition, which is one of source of performance overhead of isolation, can also be decreased. Even in this weaker model, we recommend using BTI instructions to enforce landing at intended entry points; this design can be realised using the Complets API.

5.0.0.2 Why Complets

We introduce the abstraction of complets rather than reuse the term compartments, whilst use compartmentalisation as a technique. We view compartments as primarily associated with spatial characteristics of code, i.e., the protection key tied to particular memory (POIndex in poe terminology). The complet abstraction combines spatial and temporal characteristics and hides those details behind a single interface. Thus, while the term compartment remains applicable as a general concept, complets represent a distinct, richer abstraction that can be partially mapped to compartments.

5.0.0.3 Future Work

The performance characteristics of POE2 depend strongly on microarchitectural implementation: new tables whose values must be cached, new instructions that operate on cached values, new registers, and related mechanisms. Consequently, while developing a compartmentalisation library that implements complets using poe1 is a reasonable proof of concept, its performance hardly can be extrapolated to poe2. Future work will implement poe2 on a suitable platform for performance evaluation and will exercise the proposed model with real-world scenarios.

References↩︎

[1]
Intel® 64 and IA-32 architectures software developer’s manual. Intel Corporation, 2018.
[2]
Arm Ltd., Accessed: 2026-02-12Permission indirection and permission overlay extensions. https://developer.arm.com/documentation/102376/0200/Permission-indirection-and-permission-overlay-extensions, 2020.
[3]
H. Lefeuvre, N. Dautenhahn, D. Chisnall, and P. Olivier, SoK: Software Compartmentalization ,” in 2025 IEEE symposium on security and privacy (SP), May 2025, pp. 3107–3126, doi: 10.1109/SP61157.2025.00075.
[4]
J. Gu, H. Li, W. Li, Y. Xia, and H. Chen, EPK: Scalable and efficient memory protection keys,” in 2022 USENIX annual technical conference (USENIX ATC 22), Jul. 2022, pp. 609–624, [Online]. Available: https://www.usenix.org/conference/atc22/presentation/gu-jinyu.
[5]
J. Gu et al., Harmonizing Performance and Isolation in Microkernels with Efficient Intra-kernel Isolation and Communication,” in 2020 USENIX annual technical conference (USENIX ATC 20), Jul. 2020, pp. 401–417.
[6]
M. Hedayati et al., “Hodor: Intra-Process isolation for High-Throughput data plane libraries,” in 2019 USENIX annual technical conference (USENIX ATC 19), Jul. 2019, pp. 489–504, [Online]. Available: http://www.usenix.org/conference/atc19/presentation/hedayati-hodor.
[7]
A. Vahldiek-Oberwagner, E. Elnikety, N. O. Duarte, M. Sammler, P. Druschel, and D. Garg, ERIM: Secure, Efficient In-process Isolation with Protection Keys (MPK),” in 28th USENIX security symposium (USENIX security 19), Aug. 2019, pp. 1221–1238.
[8]
[9]
Arm Ltd., Accessed: 2026-02-12“AArch64 translation: S1POE2.” https://developer.arm.com/documentation/111108/2025-12/Shared-Pseudocode/aarch64-translation-s1poe2?lang=en, 2025.
[10]
Arm Ltd., Accessed: 2026-02-12Fixed Virtual Platforms.” https://trustedfirmware-a.readthedocs.io/en/latest/plat/arm/fvp/index.html, 2025.
[11]
S. Gravani, M. Hedayati, J. Criswell, and M. L. Scott, “IskiOS: Lightweight defense against kernel-level code-reuse attacks,” arXiv preprint arXiv:1903.04654, 2019.
[12]
S. Park, S. Lee, W. Xu, H. Moon, and T. Kim, libmpk: Software Abstraction for Intel Memory Protection Keys (Intel MPK),” in 2019 USENIX annual technical conference (USENIX ATC 19), Jul. 2019, pp. 241–254.
[13]
K. Gudka et al., Clean Application Compartmentalization with SOAAP,” in Proceedings of the 22nd ACM SIGSAC conference on computer and communications security, 2015, pp. 1016–1031.
[14]
J. Litton, A. Vahldiek-Oberwagner, E. Elnikety, D. Garg, B. Bhattacharjee, and P. Druschel, “Light-weight contexts: An OS abstraction for safety and performance,” in Proceedings of the 12th USENIX conference on operating systems design and implementation, 2016, pp. 49–64.
[15]
Y. Liu, T. Zhou, K. Chen, H. Chen, and Y. Xia, “Thwarting memory disclosure with efficient hypervisor-enforced intra-domain isolation,” in Proceedings of the 22nd ACM SIGSAC conference on computer and communications security, 2015, pp. 1607–1619, doi: 10.1145/2810103.2813690.
[16]
V. A. Sartakov, L. Vilanova, and P. Pietzuch, “CubicleOS: A library OS with software componentisation for practical isolation,” in Proceedings of the 26th ACM international conference on architectural support for programming languages and operating systems, 2021, pp. 546–558, doi: 10.1145/3445814.3446731.
[17]
H. Lefeuvre et al., “FlexOS: Towards flexible OS isolation,” in Proceedings of the 27th ACM international conference on architectural support for programming languages and operating systems, 2022, pp. 467–482, doi: 10.1145/3503222.3507759.
[18]
Y. Chen, S. Reymondjohnson, Z. Sun, and L. Lu, “Shreds: Fine-grained execution units with private memory,” in 2016 IEEE symposium on security and privacy (SP), 2016, pp. 56–71.
[19]
M. Xie et al., “CETIS: Retrofitting intel CET for generic and efficient intra-process memory isolation,” in Proceedings of the 2022 ACM SIGSAC conference on computer and communications security, 2022, pp. 2989–3002, doi: 10.1145/3548606.3559344.
[20]
Y. Zheng et al., “Extending applications safely and efficiently,” in Proceedings of the 19th USENIX conference on operating systems design and implementation, 2025.
[21]
D. Brumley and D. Song, Privtrans: Automatically Partitioning Programs for Privilege Separation,” in 13th USENIX security symposium (USENIX security 04), Aug. 2004.
[22]
A. Bittau, P. Marchenko, M. Handley, and B. Karp, Wedge: Splitting Applications into Reduced-Privilege Compartments,” in 5th USENIX symposium on networked systems design and implementation (USENIX NSDI 08), Apr. 2008, [Online]. Available: https://www.usenix.org/conference/nsdi-08/wedge-splitting-applications-reduced-privilege-compartments.
[23]
I. Corp, Software Guard Extensions Programming Reference, Ref. 329298-002US.” https://software.intel.com/sites/default/files/managed/48/88/329298-002.pdf, Oct. 2014, [Online]. Available: https://software.intel.com/sites/default/files/managed/48/88/329298-002.pdf.

  1. Next and following, POE will stand either for the general term Permission Overlay Extension or refer specifically to POE1↩︎

  2. Other exception levels provide trap‑like domain transition mechanisms based on TENTER and TEXIT. This work considers poe2 only in EL0.↩︎