|
1 | | -<h1>LFI <img src="assets/lfi-logo.svg" alt="logo" width="30px"/></h1> |
| 1 | +# Lightweight Fault Isolation |
2 | 2 |
|
3 | | - |
4 | | -[](https://github.com/zyedidia/lfi/blob/master/LICENSE) |
| 3 | +Welcome to the home of the Lightweight Fault Isolation (LFI) project. The |
| 4 | +project is hosted on GitHub in the https://github.com/lfi-project organization. |
| 5 | +This repository contains information and tutorials about how to use LFI. |
5 | 6 |
|
6 | | -LFI (Lightweight Fault Isolation) is a performant and secure software |
7 | | -sandboxing system targeting the ARM64 and x86-64 architectures. LFI allows you |
8 | | -to run ~64K (ARM64) or ~3K (x86-64) sandboxes in a single address space while |
9 | | -guaranteeing that the sandboxes cannot read or write each other's memory. Each |
10 | | -sandbox may be given up to 4GiB of memory. These sandboxes are very efficient: |
11 | | -on ARM64 they run with roughly 7% overhead compared to native code when |
12 | | -sandboxing reads and writes, and 1.5% overhead when only sandboxing writes |
13 | | -(x86-64 has slightly higher overheads). Since all sandboxes exist in the same |
14 | | -address space, context switches do not require changing the CPU's privilege |
15 | | -level (i.e., transitioning to kernel mode). |
16 | | - |
17 | | -The code for LFI is currently being reorganized as part of a transition to a |
18 | | -production-ready project. Please see https://github.com/lfi-project and stay |
19 | | -tuned for more details. |
20 | | - |
21 | | -# Tools |
| 7 | +The GitHub organization has several repositories for the different components of LFI: |
22 | 8 |
|
23 | | -The LFI project provides the following tools: |
| 9 | +* [`lfi-runtime`](https://github.com/lfi-project/lfi-runtime): the runtime responsible for loading LFI programs, handling host calls, and running verification. |
| 10 | +* [`lfi-verifier`](https://github.com/lfi-project/lfi-verifier): the LFI verifier. |
| 11 | +* [`lfi-rewriter`](https://github.com/lfi-project/lfi-rewriter): the LFI rewriter for GNU assembly, and a post-linker tool for performing binary transformations after linking (only necessary on x86-64). |
| 12 | +* [`lfi-bind`](https://github.com/lfi-project/lfi-bind): a tool for generating trampolines and initialization routines for running libraries inside of LFI sandboxes. |
| 13 | +* [`lfi-llvm-toolchain`](https://github.com/lfi-project/lfi-llvm-toolchain): scripts for building an LLVM-based LFI compiler toolchain, along with a sysroot including compiler-rt, musl, libc++, and mimalloc. |
| 14 | +* [`llvm-project`](https://github.com/lfi-project/llvm-project): our development fork of the LLVM project. |
| 15 | +* [`lfi-specification`](https://github.com/lfi-project/lfi-specification): the LaTeX sources for the LFI specification document. |
24 | 16 |
|
25 | | -* `lfi-leg`: reads a `.s` file, and produces an LFI-compatible `.s` file. |
26 | | -* `lfi-verify`: verifies ELF binaries for LFI-compatibility. |
27 | | -* `lfi-postlink`: patches binaries after linking (required for certain x86-64 optimizations). |
28 | | -* `lfi-run`: runs an LFI-compatible binary. |
| 17 | +As a research project, LFI implementation initially existed in the |
| 18 | +https://github.com/zyedidia/lfi repository. See the `lfi-research` branch of |
| 19 | +this repository for that version. |
29 | 20 |
|
30 | | -# Installation |
| 21 | +# What is LFI? |
31 | 22 |
|
32 | | -There are two components to LFI: a compiler toolchain that can build |
33 | | -LFI-compatible binaries, and a runtime library for creating, verifying, and |
34 | | -managing sandboxes. For either of these components, you can either use prebuilt |
35 | | -versions provided with releases, or build from source. |
36 | | - |
37 | | -## Prebuilt distribution |
38 | | - |
39 | | -Prebuilt toolchains are provided in the GitHub releases: |
40 | | -https://github.com/zyedidia/lfi/releases/. The prebuilt toolchain includes a |
41 | | -full Clang compiler based on LLVM 19. |
42 | | - |
43 | | -When you download a prebuilt toolchain, you will see the following directories: |
| 23 | +LFI (Lightweight Fault Isolation) is a performant and secure software |
| 24 | +sandboxing system targeting the Arm64 and x86-64 architectures. LFI allows you |
| 25 | +to run ~64K (Arm64) or ~3K (x86-64) sandboxes in a single address space while |
| 26 | +guaranteeing that the sandboxes cannot read or write each other's memory. Each |
| 27 | +sandbox may be given up to 4GiB of memory. These sandboxes are very efficient: |
| 28 | +they run with roughly 7% (Arm64)/8% (x86-64) overhead compared to native code |
| 29 | +when sandboxing reads and writes, and 1.5% (Arm64)/6% (x86-64) overhead when |
| 30 | +only sandboxing writes, measured on SPEC 2017. Since all sandboxes exist in the |
| 31 | +same address space, context switches do not require changing the CPU's |
| 32 | +privilege level (i.e., transitioning to kernel mode). |
44 | 33 |
|
45 | | -* `lfi-bin/`: contains the LFI rewriter, verifier, and runtime. Put this on |
46 | | - your `PATH`. |
47 | | -* `bin/`: contains the LLVM compiler binaries. |
48 | | -* `sysroot/`: contains the LFI sysroot, including libc, libc++, a dynamic |
49 | | - linker, and additional runtime support libraries. |
| 34 | +# Getting Started |
50 | 35 |
|
51 | | -## Building from source |
| 36 | +To get started with LFI, you'll need an LFI compiler. You can get one from |
| 37 | +https://github.com/lfi-project/lfi-llvm-toolchain. Prebuilt compilers aarch64 |
| 38 | +and x86-64 compilers are hosted in the releases: |
| 39 | +https://github.com/lfi-project/lfi-llvm-toolchain/releases/tag/v0.9. |
52 | 40 |
|
53 | | -To build and install the LFI tools, run the following commands: |
| 41 | +After opening the archive, you should see a directory structure like this: |
54 | 42 |
|
55 | 43 | ``` |
56 | | -meson setup build --prefix=$PWD/install |
57 | | -cd build |
58 | | -ninja install |
| 44 | +├── bin |
| 45 | +│ ├── LLVM tools (clang, lld, ...) |
| 46 | +├── include |
| 47 | +├── lfi-bin |
| 48 | +│ ├── lfi-postlink |
| 49 | +│ ├── lfi-rewrite |
| 50 | +│ ├── lfi-run |
| 51 | +│ ├── lfi-verify |
| 52 | +│ ├── ARCH-lfi-linux-musl-clang -> ../bin/clang |
| 53 | +│ └── ARCH-lfi-linux-musl-clang++ -> ../bin/clang++ |
| 54 | +├── lfi-clang |
| 55 | +│ ├── ARCH-lfi-linux-musl-clang -> ../bin/clang |
| 56 | +│ └── ARCH-lfi-linux-musl-clang++ -> ../bin/clang++ |
| 57 | +├── lib |
| 58 | +├── libexec |
| 59 | +├── share |
| 60 | +└── sysroot |
59 | 61 | ``` |
60 | 62 |
|
61 | | -You will find the generated binaries, libraries, and headers in `$PWD/install` |
62 | | -(or in your prefix of choice). Before building a compiler toolchain, you should |
63 | | -make sure the installed `bin` directory is on your `PATH`. |
64 | | - |
65 | | -Next, you might want to build a compiler toolchain based on LLVM or GCC. For |
66 | | -instructions and scripts, see [lfi-gcc](https://github.com/zyedidia/lfi-gcc) |
67 | | -and [lfi-llvm](https://github.com/zyedidia/lfi-llvm-toolchain). |
| 63 | +You'll want to add the `lfi-bin` directory to your `PATH`. If you already have |
| 64 | +the LFI tools installed via a separate installation, consider only adding |
| 65 | +`lfi-clang` to your `PATH` instead. |
68 | 66 |
|
69 | | -# Example |
| 67 | +Once you have a compiler, you should be able to build a simple C or C++ program: |
70 | 68 |
|
71 | | -Once you have installed all the tools, you can build simple programs. |
72 | | - |
73 | | -``` |
| 69 | +```c |
74 | 70 | #include <stdio.h> |
75 | 71 | int main() { |
76 | 72 | printf("Hello from LFI\n"); |
77 | 73 | return 0; |
78 | 74 | } |
79 | 75 | ``` |
80 | 76 |
|
81 | | -With Clang: |
| 77 | +Compile and run using `lfi-run`: |
82 | 78 |
|
83 | 79 | ``` |
84 | 80 | $ aarch64-lfi-linux-musl-clang hello.c -O2 -o hello -static-pie |
85 | | -$ lfi-verify hello # check if it verifies (also performed by lfi-run) |
86 | | -verifying test |
87 | | -verification passed (3.2 MB/s) |
88 | | -$ lfi-run hello |
| 81 | +$ lfi-run -v ./hello |
89 | 82 | Hello from LFI |
90 | 83 | ``` |
91 | 84 |
|
92 | | -And with GCC: |
| 85 | +You can use the prebuilt `lfi-run` tool from the prebuilt archive. You can also |
| 86 | +easily build the LFI runtime from source: |
93 | 87 |
|
94 | 88 | ``` |
95 | | -$ aarch64-lfi-linux-musl-gcc hello.c -O2 -o hello -static-pie |
96 | | -$ lfi-run hello |
97 | | -Hello from LFI |
| 89 | +git clone https://github.com/lfi-project/lfi-runtime |
| 90 | +cd lfi-runtime |
| 91 | +meson setup build |
| 92 | +cd build |
| 93 | +ninja |
| 94 | +./tools/lfi-run/lfi-run ... |
98 | 95 | ``` |
99 | 96 |
|
100 | | -# Advanced Usage |
| 97 | +The `lfi-run` tool is primarily meant for benchmarking and testing. It does not |
| 98 | +run verification by default (pass `-v` to enable), and it allows full |
| 99 | +filesystem access by default. See the usage (`lfi-run --help`) for more |
| 100 | +information. |
101 | 101 |
|
102 | | -The `lfi-leg` rewriter tool supports options for configuring the sandboxing approach. |
| 102 | +# Next Steps |
103 | 103 |
|
104 | | -``` |
105 | | -Usage: lfi-leg [OPTION...] INPUT |
106 | | -lfi-gen: rewrite assembly files to be compatible with LFI |
107 | | -
|
108 | | - -a, --arch=ARCH Set the target architecture (arm64,amd64) |
109 | | - --cfi=TYPE Select CFI mechanism (bundle16,bundle32) |
110 | | - --no-guard-elim Do not run redundant guard elimination |
111 | | - --no-segue Do not use segment register to store the sandbox |
112 | | - base |
113 | | - -o, --output=FILE Output to FILE instead of standard output |
114 | | - --poc Produce position-oblivious code (implies |
115 | | - --sys-external) |
116 | | - --single-thread Specify single-threaded target |
117 | | - --sys-external Store runtime call table outside sandbox |
118 | | - -s, --sandbox=TYPE Select sandbox type |
119 | | - (full,stores,bundle-jumps,none) |
120 | | - -?, --help Give this help list |
121 | | - --usage Give a short usage message |
122 | | -``` |
| 104 | +After building and running your first LFI program, you may want to pursue |
| 105 | +several further directions. See the following guides for some suggestions: |
123 | 106 |
|
124 | | -Some notes: |
125 | | - |
126 | | -* By default, LFI on x86-64 uses 16-byte bundles. We have measured better |
127 | | - performance with 32-byte bundles on Intel machines, and better performance |
128 | | - with 16-byte bundles on AMD machines. LFI does not use bundles on Arm64. |
129 | | -* The `--sandbox` option can be used to configure isolation granularity. With |
130 | | - `stores`, only stores and control-flow is sandboxed. Programs are allowed to |
131 | | - read outside of their memory. With `bundle-jumps`, LFI only enforces that |
132 | | - bundles are the targets of jumps, but does not enforce that jumps are |
133 | | - constrained to the sandbox, and does not enforce memory isolation. |
134 | | -* The `--single-thread` option in combination with `--sandbox=bundle-jumps` |
135 | | - allows for more efficient return sequences, but is only applicable when |
136 | | - sandboxed programs are single-threaded. |
137 | | - |
138 | | -# Technical Summary |
139 | | - |
140 | | -The LFI sandboxer only accepts ELF binaries that pass a verification step to |
141 | | -ensure they are safe to run. This verifier works by analyzing binary machine |
142 | | -code to ensure that the program will not access memory outside of its 4GiB |
143 | | -region or execute any system calls or other "unsafe" instructions. The verifier |
144 | | -is extremely simple, and is implemented in only a few hundred lines of code, |
145 | | -located in `lfi-verify/arm64/verify.c`. Thanks to the verifier, the compiler |
146 | | -used to generate the code is untrusted, so bugs in LLVM or GCC cannot cause |
147 | | -security vulnerabilities. This approach is both more secure and more performant |
148 | | -than current approaches that rely on a trusted compiler like Cranelift. The |
149 | | -verifier is also efficient, and can process machine code at a throughput of |
150 | | -100+ MiB/s on a Macbook Air. |
151 | | - |
152 | | -In addition, LFI binaries may be generated by any LFI-compatible compiler |
153 | | -toolchain. LFI-compatible Clang and GCC toolchains are provided. |
154 | | - |
155 | | -The core of the LFI toolchain is an assembly rewriter that reads arbitrary GNU |
156 | | -assembly files (`.s`) and produces assembly files that will pass verification |
157 | | -when compiled and linked. This rewriter is implemented as a set of PEG parsers |
158 | | -that are compiled to C using Leg, and consists of roughly 2,000 lines of code |
159 | | -total (ARM64 and x86-64). It is located in `lfi-leg/`. |
160 | | - |
161 | | -LFI-compatible programs are performant: on the SPEC 2017 benchmark suite, we |
162 | | -measured a runtime overhead of 7% and a code size overhead of 14% for full |
163 | | -isolation. This compares well with LLVM-based ahead-of-time WebAssembly |
164 | | -compilers, which incur upwards of 20% runtime overhead. Additionally, LFI can |
165 | | -be used for pure fault isolation, where sandboxes may read, but not write, |
166 | | -each other's memory. In this case, we measured a runtime overhead of around |
167 | | -1.5%. LFI also compares well with Native Client (NaCl), the most widely-known |
168 | | -prior SFI system, which incurred around 16% overhead for full isolation and 7% |
169 | | -overhead for just stores on x86-64 SPEC benchmarks. NaCl never received Arm64 |
170 | | -support before being deprecated. |
171 | | - |
172 | | -LFI is also secure: the compiler toolchain used to |
173 | | -produce LFI-compatible programs is not a part of the trusted code base, and LFI |
174 | | -is significantly more Spectre-resistant compared to WebAssembly. |
175 | | - |
176 | | -LFI supports all source-level language features and targets the ARMv8.0-A ISA |
177 | | -(including SIMD) plus the ARMv8.1 LSE extension. |
178 | | - |
179 | | -The last component of an LFI system is the runtime, which loads programs and |
180 | | -handles runtime calls (e.g., syscalls) on their behalf. To create your own |
181 | | -runtime, you can use `liblfi`, which provides utility functions for creating and |
182 | | -running sandboxes, and handling runtime calls (it is up to you what runtime |
183 | | -calls are available and what they do). |
184 | | - |
185 | | -The `lfi-run` program is an example LFI runtime that behaves like a subset of |
186 | | -Linux, and can be used to run many programs compiled for Linux with an LFI |
187 | | -toolchain. This runtime is useful for running benchmarks such as SPEC 2017. |
188 | | - |
189 | | -LFI is currently in development and is a research project. |
190 | | - |
191 | | -# Library Sandboxing |
192 | | - |
193 | | -By default, the LFI compiler produces executables that you can run in a |
194 | | -sandboxed environment. To adapt this model to library sandboxing, a tool called |
195 | | -[lfi-bind](https://github.com/zyedidia/lfi-bind) can be used to generate bindings for |
196 | | -a library compiled with LFI so that functions within the sandbox can be invoked |
197 | | -by a host. Since the LFI sandbox runs within a single process, invoking a |
198 | | -sandboxed function is very cheap -- roughly 20-40 cycles. |
199 | | - |
200 | | -## Specification |
201 | | - |
202 | | -LFI's current sandboxing schemes for Arm64 and x86-64 are documented in the |
203 | | -[LFI specification](https://www.scs.stanford.edu/~zyedidia/docs/lfi/lfi-spec.pdf). |
204 | | - |
205 | | -# Publication |
206 | | - |
207 | | -Zachary Yedidia. "Lightweight Fault Isolation: Practical, Efficient, and Secure Software Sandboxing." ASPLOS 2024. [Link](https://zyedidia.github.io/papers/lfi_asplos24.pdf). |
208 | | - |
209 | | -# Roadmap |
210 | | - |
211 | | -The LFI project is currently under development. Here are some directions we are |
212 | | -interested in (some of which are being actively developed): |
213 | | - |
214 | | -* Integration with library sandboxing tools. |
215 | | -* New capabilities and full security for the runtime. |
216 | | -* Optimized integration with WebAssembly (efficiently run WebAssembly inside LFI). |
217 | | -* Support for dynamic recompilation to other architectures. |
218 | | -* Native support for RISC-V. |
219 | | -* Instrumentation of static binaries, so you don't need to recompile your program. |
220 | | -* Support for Arm software context IDs for mitigating Spectre attacks (requires |
221 | | - modifications to Linux, which Arm will hopefully implement soon). |
| 107 | +* Setting up benchmarking with `lfi-bench` and SPEC 2017: [guide](docs/BENCHMARKING.md). |
| 108 | +* Using LFI to sandbox libraries: [guide](docs/LIBRARIES.md). |
| 109 | +* Learning more about how LFI works: [guide](docs/DETAILS.md). |
0 commit comments