Skip to content

Commit f47d136

Browse files
committed
[LFI] Add LFI.rst documentation
1 parent 812b812 commit f47d136

File tree

1 file changed

+387
-0
lines changed

1 file changed

+387
-0
lines changed

llvm/docs/LFI.rst

Lines changed: 387 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,387 @@
1+
=========================================
2+
Lightweight Fault Isolation (LFI) in LLVM
3+
=========================================
4+
5+
.. contents::
6+
:local:
7+
8+
Introduction
9+
++++++++++++
10+
11+
Lightweight Fault Isolation (LFI) is a compiler-based sandboxing technology for
12+
native code. Like WebAssembly and Native Client, LFI isolates sandboxed code in-process
13+
(i.e., in the same address space as a host application).
14+
15+
LFI is designed from the ground up to sandbox existing code, such as C/C++
16+
libraries (including assembly code) and device drivers.
17+
18+
LFI aims for the following goals:
19+
20+
* Compatibility: LFI can be used to sandbox nearly all existing C/C++/assembly
21+
libraries unmodified (they just need to be recompiled). Sandboxed libraries
22+
work with existing system call interfaces, and are compatible with existing
23+
development tools such as profilers, debuggers, and sanitizers.
24+
* Performance: LFI aims for minimal overhead vs. unsandboxed code.
25+
* Security: The LFI runtime and compiler elements aim to be simple and
26+
verifiable when possible.
27+
* Usability: LFI aims to make it easy as possible to used retrofit sandboxing,
28+
i.e., to migrate from unsandboxed to sandboxed libraries with minimal effort.
29+
30+
When building a program for the LFI target the compiler is designed to ensure
31+
that the program will only be able to access memory within a limited region of
32+
the virtual address space, starting from where the program is loaded (the
33+
current design sets this region to a size of 4GiB of virtual memory). Programs
34+
built for the LFI target are restricted to using a subset of the instruction
35+
set, designed so that the programs can be soundly confined to their sandbox
36+
region. LFI programs must run inside of an "emulator" (usually called the LFI
37+
runtime), responsible for initializing the sandbox region, loading the program,
38+
and servicing system call requests, or other forms of runtime calls.
39+
40+
LFI uses an architecture-specific sandboxing scheme based on the general
41+
technique of Software-Based Fault Isolation (SFI). Initial support for LFI in
42+
LLVM is focused on the AArch64 platform, with x86-64 support planned for the
43+
future. The initial version of LFI for AArch64 is designed to support the
44+
Armv8.1 AArch64 architecture.
45+
46+
See `https://github.com/lfi-project <https://github.com/lfi-project/>`__ for
47+
details about the LFI project and additional software needed to run LFI
48+
programs.
49+
50+
Compiler Requirements
51+
+++++++++++++++++++++
52+
53+
When building for the ``aarch64_lfi`` target, the compiler must restrict use of
54+
the instruction set to a subset of instructions, which are known to be safe
55+
from a sandboxing perspective. To do this, we apply a set of simple rewrites at
56+
the assembly language level to transform standard native AArch64 assembly into
57+
LFI-compatible AArch64 assembly.
58+
59+
These rewrites (also called "expansions") are applied at the very end of the
60+
LLVM compilation pipeline (during the assembler step). This allows the rewrites
61+
to be applied to hand-written assembly, including inline assembly.
62+
63+
Compiler Options
64+
================
65+
66+
The LFI target has several configuration options.
67+
68+
* ``+lfi-stores``: create a "stores-only" sandbox, where rewrites are not applied to loads.
69+
* ``+lfi-jumps``: create a "jumps-only" sandbox, where rewrites are not applied to loads/stores.
70+
71+
Reserved Registers
72+
==================
73+
74+
The LFI target uses a custom ABI that reserves additional registers for the
75+
platform. The registers are listed below, along with the security invariant
76+
that must be maintained.
77+
78+
* ``x27``: always holds the sandbox base address.
79+
* ``x28``: always holds an address within the sandbox.
80+
* ``sp``: always holds an address within the sandbox.
81+
* ``x30``: always holds an address within the sandbox.
82+
* ``x26``: scratch register.
83+
* ``x25``: points to a thread-local virtual register file for storing runtime context information.
84+
85+
Linker Support
86+
==============
87+
88+
In the initial version, LFI only supports static linking, and only supports
89+
creating ``static-pie`` binaries. There is nothing that fundamentally precludes
90+
support for dynamic linking on the LFI target, but such support would require
91+
that the code generated by the linker for PLT entries be slightly modified in
92+
order to conform to the LFI architecture subset.
93+
94+
Assembly Rewrites
95+
=================
96+
97+
Terminology
98+
~~~~~~~~~~~
99+
100+
In the following assembly rewrites, some shorthand is used.
101+
102+
* ``xN`` or ``wN``: refers to any general-purpose non-reserved register.
103+
* ``{a,b,c}``: matches any of ``a``, ``b``, or ``c``.
104+
* ``LDSTr``: a load/store instruction that supports register-register addressing modes, with one source/destination register.
105+
* ``LDSTx``: a load/store instruction not matched by ``LDSTr``.
106+
107+
Control flow
108+
~~~~~~~~~~~~
109+
110+
Indirect branches get rewritten to branch through register ``x28``, which must
111+
always contain an address within the sandbox. An ``add`` is used to safely load
112+
``x28`` with the destination address. Since ``ret`` uses ``x30`` by default,
113+
which already must contain an address within the sandbox, it does not require
114+
any rewrite.
115+
116+
+--------------------+---------------------------+
117+
| Original | Rewritten |
118+
+--------------------+---------------------------+
119+
| .. code-block:: | .. code-block:: |
120+
| | |
121+
| {br,blr,ret} xN | add x28, x27, wN, uxtw |
122+
| | {br,blr,ret} x28 |
123+
| | |
124+
+--------------------+---------------------------+
125+
| .. code-block:: | .. code-block:: |
126+
| | |
127+
| ret | ret |
128+
| | |
129+
+--------------------+---------------------------+
130+
131+
Memory accesses
132+
~~~~~~~~~~~~~~~
133+
134+
Memory accesses are rewritten to use the ``[x27, wM, uxtw]`` addressing mode if
135+
it is available, which is automatically safe. Otherwise, rewrites fall back to
136+
using ``x28`` along with an instruction to safely load it with the target
137+
address.
138+
139+
+---------------------------------+-------------------------------+
140+
| Original | Rewritten |
141+
+---------------------------------+-------------------------------+
142+
| .. code-block:: | .. code-block:: |
143+
| | |
144+
| LDSTr xN, [xM] | LDSTr xN, [x27, wM, uxtw] |
145+
| | |
146+
+---------------------------------+-------------------------------+
147+
| .. code-block:: | .. code-block:: |
148+
| | |
149+
| LDSTr xN, [xM, #I] | add x28, x27, wM, uxtw |
150+
| | LDSTr xN, [x28, #I] |
151+
| | |
152+
+---------------------------------+-------------------------------+
153+
| .. code-block:: | .. code-block:: |
154+
| | |
155+
| LDSTr xN, [xM, #I]! | add xM, xM, #I |
156+
| | LDSTr xN, [x27, wM, uxtw] |
157+
| | |
158+
+---------------------------------+-------------------------------+
159+
| .. code-block:: | .. code-block:: |
160+
| | |
161+
| LDSTr xN, [xM], #I | LDSTr xN, [x27, wM, uxtw] |
162+
| | add xM, xM, #I |
163+
| | |
164+
+---------------------------------+-------------------------------+
165+
| .. code-block:: | .. code-block:: |
166+
| | |
167+
| LDSTr xN, [xM1, xM2] | add x26, xM1, xM2 |
168+
| | LDSTr xN, [x27, w26, uxtw] |
169+
| | |
170+
+---------------------------------+-------------------------------+
171+
| .. code-block:: | .. code-block:: |
172+
| | |
173+
| LDSTr xN, [xM1, xM2, MOD #I] | add x26, xM1, xM2, MOD #I |
174+
| | LDSTr xN, [x27, w26, uxtw] |
175+
| | |
176+
+---------------------------------+-------------------------------+
177+
| .. code-block:: | .. code-block:: |
178+
| | |
179+
| LDSTx ..., [xM] | add x28, x27, wM, uxtw |
180+
| | LDSTx ..., [x28] |
181+
| | |
182+
+---------------------------------+-------------------------------+
183+
| .. code-block:: | .. code-block:: |
184+
| | |
185+
| LDSTx ..., [xM, #I] | add x28, x27, wM, uxtw |
186+
| | LDSTx ..., [x28, #I] |
187+
| | |
188+
+---------------------------------+-------------------------------+
189+
| .. code-block:: | .. code-block:: |
190+
| | |
191+
| LDSTx ..., [xM, #I]! | add x28, x27, wM, uxtw |
192+
| | LDSTx ..., [x28, #I] |
193+
| | add xM, xM, #I |
194+
| | |
195+
+---------------------------------+-------------------------------+
196+
| .. code-block:: | .. code-block:: |
197+
| | |
198+
| LDSTx ..., [xM], #I | add x28, x27, wM, uxtw |
199+
| | LDSTx ..., [x28] |
200+
| | add xM, xM, #I |
201+
| | |
202+
+---------------------------------+-------------------------------+
203+
| .. code-block:: | .. code-block:: |
204+
| | |
205+
| LDSTx ..., [xM1], xM2 | add x28, x27, wM1, uxtw |
206+
| | LDSTx ..., [x28] |
207+
| | add xM1, xM1, xM2 |
208+
| | |
209+
+---------------------------------+-------------------------------+
210+
211+
Stack pointer modification
212+
~~~~~~~~~~~~~~~~~~
213+
214+
When the stack pointer is modified, we write the modified value to a temporary,
215+
before loading it back into ``sp`` with a safe ``add``.
216+
217+
+------------------------------+-------------------------------+
218+
| Original | Rewritten |
219+
+------------------------------+-------------------------------+
220+
| .. code-block:: | .. code-block:: |
221+
| | |
222+
| mov sp, xN | add sp, x27, wN, uxtw |
223+
| | |
224+
+------------------------------+-------------------------------+
225+
| .. code-block:: | .. code-block:: |
226+
| | |
227+
| {add,sub} sp, sp, {#I,xN} | {add,sub} x26, sp, {#I,xN} |
228+
| | add sp, x27, w26, uxtw |
229+
| | |
230+
+------------------------------+-------------------------------+
231+
232+
Link register modification
233+
~~~~~~~~~~~~~~~~~~~~~~~~~~~
234+
235+
When the link register is modified, we write the modified value to a
236+
temporary, before loading it back into ``x30`` with a safe ``add``.
237+
238+
+-----------------------+----------------------------+
239+
| Original | Rewritten |
240+
+-----------------------+----------------------------+
241+
| .. code-block:: | .. code-block:: |
242+
| | |
243+
| ldr x30, [...] | ldr x26, [...] |
244+
| | add x30, x27, w26, uxtw |
245+
| | |
246+
+-----------------------+----------------------------+
247+
| .. code-block:: | .. code-block:: |
248+
| | |
249+
| ldp xN, x30, [...] | ldp xN, x26, [...] |
250+
| | add x30, x27, w26, uxtw |
251+
| | |
252+
+-----------------------+----------------------------+
253+
| .. code-block:: | .. code-block:: |
254+
| | |
255+
| ldp x30, xN, [...] | ldp x26, xN, [...] |
256+
| | add x30, x27, w26, uxtw |
257+
| | |
258+
+-----------------------+----------------------------+
259+
260+
System instructions
261+
~~~~~~~~~~~~~~~~~~~
262+
263+
System calls are rewritten into a sequence that loads the address of the first
264+
runtime call entrypoint and jumps to it. The runtime call entrypoint table is
265+
stored at the start of the sandbox, so it can be referenced by ``x27``. The
266+
rewrite also saves and restores the link register, since it is used for
267+
branching into the runtime.
268+
269+
+-----------------+----------------------------+
270+
| Original | Rewritten |
271+
+-----------------+----------------------------+
272+
| .. code-block:: | .. code-block:: |
273+
| | |
274+
| svc #0 | mov w26, w30 |
275+
| | ldr x30, [x27] |
276+
| | blr x30 |
277+
| | add x30, x27, w26, uxtw |
278+
| | |
279+
+-----------------+----------------------------+
280+
281+
Thread-local storage
282+
~~~~~~~~~~~~~~~~~~~~
283+
284+
TLS accesses are rewritten into accesses offset from ``x25``, which is a
285+
reserved register that points to a virtual register file, with a location for
286+
storing the sandbox's thread pointer. ``TP`` is the offset into that virtual
287+
register file where the thread pointer is stored.
288+
289+
+----------------------+-----------------------+
290+
| Original | Rewritten |
291+
+----------------------+-----------------------+
292+
| .. code-block:: | .. code-block:: |
293+
| | |
294+
| mrs xN, tpidr_el0 | ldr xN, [x25, #TP] |
295+
| | |
296+
+----------------------+-----------------------+
297+
| .. code-block:: | .. code-block:: |
298+
| | |
299+
| mrs tpidr_el0, xN | str xN, [x25, #TP] |
300+
| | |
301+
+----------------------+-----------------------+
302+
303+
Optimizations
304+
=============
305+
306+
Basic guard elimination
307+
~~~~~~~~~~~~~~~~~~~~~~~
308+
309+
If a register is guarded multiple times in the same basic block without any
310+
modifications to it during the intervening instructions, then subsequent guards
311+
can be removed.
312+
313+
+---------------------------+---------------------------+
314+
| Original | Rewritten |
315+
+---------------------------+---------------------------+
316+
| .. code-block:: | .. code-block:: |
317+
| | |
318+
| add x28, x27, wN, uxtw | add x28, x27, wN, uxtw |
319+
| ldur xN, [x28] | ldur xN, [x28] |
320+
| add x28, x27, wN, uxtw | ldur xN, [x28, #8] |
321+
| ldur xN, [x28, #8] | ldur xN, [x28, #16] |
322+
| add x28, x27, wN, uxtw | |
323+
| ldur xN, [x28, #16] | |
324+
| | |
325+
+---------------------------+---------------------------+
326+
327+
Stack guard elimination
328+
~~~~~~~~~~~~~~~~~~~~~~~
329+
330+
**Note**: this optimization has not been implemented.
331+
332+
If the stack pointer is modified by adding/subtracting a small immediate, and
333+
then later used to perform a memory access without any intervening jumps, then
334+
the guard on the stack pointer modification can be removed. This is because the
335+
load/store is guaranteed to trap if the stack pointer has been moved outside of
336+
the sandbox region.
337+
338+
+---------------------------+---------------------------+
339+
| Original | Rewritten |
340+
+---------------------------+---------------------------+
341+
| .. code-block:: | .. code-block:: |
342+
| | |
343+
| add x26, sp, #8 | add sp, sp, #8 |
344+
| add sp, x27, w26, uxtw | ... (same basic block) |
345+
| ... (same basic block) | ldr xN, [sp] |
346+
| ldr xN, [sp] | |
347+
| | |
348+
+---------------------------+---------------------------+
349+
350+
Guard hoisting
351+
~~~~~~~~~~~~~~
352+
353+
**Note**: this optimization has not been implemented.
354+
355+
In certain cases, guards may be hoisted outside of loops.
356+
357+
+-----------------------+-------------------------------+
358+
| Original | Rewritten |
359+
+-----------------------+-------------------------------+
360+
| .. code-block:: | .. code-block:: |
361+
| | |
362+
| mov w8, #10 | mov w8, #10 |
363+
| mov w9, #0 | mov w9, #0 |
364+
| .loop: | add x28, x27, wM, uxtw |
365+
| add w9, w9, #1 | .loop: |
366+
| ldr xN, [xM] | add w9, w9, #1 |
367+
| cmp w9, w8 | ldr xN, [x28] |
368+
| b.lt .loop | cmp w9, w8 |
369+
| .end: | b.lt .loop |
370+
| | .end: |
371+
| | |
372+
+-----------------------+-------------------------------+
373+
374+
References
375+
++++++++++
376+
377+
For more information, please see the following resources:
378+
379+
* `LFI project page <https://github.com/lfi-project/>`__
380+
* `LFI RFC <https://discourse.llvm.org/t/rfc-lightweight-fault-isolation-lfi-efficient-native-code-sandboxing-upstream-lfi-target-and-compiler-changes/88380>`__
381+
* `LFI paper <https://zyedidia.github.io/papers/lfi_asplos24.pdf>`__
382+
383+
Contact info:
384+
385+
* Zachary Yedidia - [email protected]
386+
* Tal Garfinkel - [email protected]
387+
* Sharjeel Khan - [email protected]

0 commit comments

Comments
 (0)