LiteRSan: Rust Memory-Safety Sanitizer
- LiteRSan is a Rust-specific sanitizer that improves memory safety by precisely targeting unsafe code through static analysis and metadata-based runtime checks.
- It categorizes pointers into spatially and temporally risky groups, reducing unnecessary instrumentation and achieving lower overhead than traditional ASan-based tools.
- Integrating with LLVM and rustc, LiteRSan demonstrates significant reductions in runtime, memory, and compilation overhead while accurately detecting various memory-safety bugs.
LiteRSan is a compiler-based memory-safety sanitizer for Rust that targets the gap between Rust’s language-level safety guarantees and the residual vulnerability surface introduced by unsafe code. It combines Rust-specific static analysis with lightweight runtime metadata checks, identifies only those pointers whose dereferences may violate memory safety, and selectively instruments them with the necessary spatial or temporal checks. In the formulation of "LiteRSan: Lightweight Memory Safety Via Rust-specific Program Analysis and Selective Instrumentation" (Xia et al., 19 Sep 2025), the system is explicitly positioned against ASan-based Rust sanitizers such as ERASan and RustSan, with the claim that Rust ownership, borrowing, scoping, and compiler-inserted safety checks can be exploited to avoid broad over-instrumentation while also detecting bug classes that generic ASan mechanisms miss.
1. Problem setting and motivation
Rust is a memory-safe language, but it permits the use of unsafe code, which bypasses compiler-enforced safety checks and can introduce memory vulnerabilities (Xia et al., 19 Sep 2025). This creates a specific sanitization problem: a practical detector must handle unsafe Rust without discarding the precision that Rust’s ownership and lifetime model already provides.
The paper argues that prior Rust sanitizers inherit a C/C++-centric design through AddressSanitizer. ASan-based approaches such as ERASan and RustSan still rely on generic LLVM/SVF-style pointer analysis and classic ASan runtime mechanisms including red zones, shadow memory, and quarantine. According to the paper, this combination produces three limitations. First, generic alias analysis is too conservative for Rust, because ownership and borrowing sharply constrain aliasing; as a result, many pointers are treated as risky even when the compiler already guarantees safety. Second, the runtime machinery remains heavyweight in both time and memory. Third, ASan’s detection model is incomplete for Rust-specific failure modes: large overflows can bypass red zones, use-after-free may be missed if freed memory is reallocated after quarantine, and ownership-based temporal bugs such as cases involving mem::forget fall outside ASan’s native model (Xia et al., 19 Sep 2025).
LiteRSan is proposed to address these issues simultaneously. Its stated design is to perform Rust-specific static analysis that is aware of pointer lifetimes, identify only risky pointers, and then selectively instrument those pointers with compact metadata checks rather than broad object-level poisoning or shadow-state mechanisms.
2. Risk model and pointer taxonomy
LiteRSan organizes the Rust memory-safety problem around a pointer classification scheme. A risky pointer is defined as a pointer whose dereferences may violate memory safety. Within that set, a pointer is spatially risky if it bypasses Rust’s spatial enforcement, and temporally risky if it may outlive its referenced object. Some pointers can be both spatially and temporally risky (Xia et al., 19 Sep 2025).
A further distinction is the exposed raw pointer: a raw pointer directly used in unsafe code and therefore bypassing Rust’s safety guarantees. This distinction is operationally important. The paper emphasizes that not all raw pointers require identical treatment, because many occur inside safe abstractions and are not directly dereferenced in unsafe contexts. LiteRSan therefore avoids treating all raw pointers or all aliases as equally suspicious.
This taxonomy maps directly onto the classes of runtime checks that LiteRSan emits. Spatially risky pointers are associated with bounds and initialization validity; temporally risky pointers are associated with lifetime and ownership validity. The paper’s presentation implies that this split is not merely descriptive but structural: it determines both what metadata must be maintained and where instrumentation is inserted. A plausible implication is that LiteRSan’s overhead reduction depends as much on this classification granularity as on the later runtime implementation.
3. Rust-specific static analysis
LiteRSan’s static analysis has two major roles: restricting analysis scope to executable code and identifying exactly which pointers are spatially or temporally risky (Xia et al., 19 Sep 2025).
The first stage is reachability analysis. Starting from the entry point, LiteRSan recursively follows direct calls, address-taken functions, and indirect call targets. This prunes dead code and reduces analysis cost.
For spatial risk, LiteRSan modifies rustc code generation so that raw-pointer-related instructions in LLVM IR are annotated during MIR-to-LLVM lowering. The metadata includes !rawptr for raw-pointer-related instructions and !unsafe for instructions originating in unsafe code. The system uses MIR type information to detect raw-pointer values such as *const T and *mut T, and then tags the corresponding LLVM instructions. After that, it filters raw pointers to retain only exposed raw pointers, namely those directly used in unsafe code. The paper also identifies a small set of unsafe standard-library APIs that can break spatial safety even without explicit raw-pointer arithmetic. The listed Type (2) APIs are unchecked_add/sub/mul/neg, forward_unchecked, backward_unchecked, unchecked_shl/shr, and set_len; these are manually identified and handled individually.
For temporal risk, LiteRSan introduces a Rust-specific, inter-procedural, flow-sensitive, lifetime-aware taint analysis. An exposed raw pointer is treated as a taint source. From that source, the analysis tracks all pointers derived from it that still remain valid in scope and may refer to the same object. The paper defines a pointer derivation instruction as any operation that produces a new pointer from an existing one by assignment, computation, store/load propagation, or inter-procedural transfer via calls and returns.
The taint analysis distinguishes two raw-pointer source categories. T1 raw pointers are created from an already-owned object, such as Vec::as_ptr(), and require both backward and forward propagation. T2 raw pointers are created for a freshly allocated object and require only forward propagation. For T1 pointers, LiteRSan traces backward along the derivation chain to reconstruct ownership history, but stops propagation when ownership has been transferred or an earlier owner has been invalidated. Forward propagation then marks valid derived pointers as temporally risky if they still reference the same object.
Inter-procedural propagation handles direct calls by flowing taint from actual arguments to formal parameters and from callee returns back to callers. Indirect calls are resolved conservatively with type-based call-target analysis. The implementation is described as worklist-based, with caching of unresolved derivations and DFS-style propagation over transitive derivation chains. The paper’s central methodological claim is that this derivation- and lifetime-aware propagation is more precise than traditional may-alias reasoning in Rust because it respects ownership transfers, scoping, and invalidation.
4. Runtime metadata and selective instrumentation
LiteRSan’s runtime is built around compact metadata attached only to selected pointers rather than broad shadow-memory instrumentation (Xia et al., 19 Sep 2025). For spatially risky pointers, the system tracks three fields: Capacity, Initialized length, and Offset. Capacity is the maximum number of elements allowed in the object, initialized length records how much of the object is initialized, and offset represents the current element index or byte offset. The metadata is stored in a separate metadata map rather than in the pointer itself.
Spatial metadata is inferred by walking backward from a risky pointer to its root pointer and extracting allocation-site information. The paper identifies two cases. In the direct case, the root points to a container such as Vec<T> or array [T; N], where length or capacity are directly available. In the indirect case, the root passes through abstractions such as Box<T> or Rc<T>, which do not directly carry useful spatial metadata, so LiteRSan backtracks to the underlying allocation. Pointer arithmetic determines the offset field.
For temporally risky pointers, LiteRSan maintains metadata for may-alias relationships and ownership. Pointers that may refer to the same object are grouped into sets, and owner pointers are identified as the ones responsible for deallocation. The runtime uses two maps: a reverse map from a tainted pointer back to its taint source, and a forward map from each taint source to shared temporal metadata. Owner detection is based on pointer types and Drop implementations, with smart pointers such as Box and Rc treated as owners when they manage deallocation.
Instrumentation is organized into five classes:
- I1: pointer activation / metadata initialization
- I2: spatial metadata update
- I3: pointer deactivation / temporal metadata update
- I4: spatial safety checks
- I5: temporal safety checks
For spatially risky pointers, LiteRSan inserts I1 at definition sites, I2 at pointer arithmetic and container-modification sites, and I4 at pointer arithmetic and dereference sites. For temporally risky pointers, it inserts I1 at definition sites, I5 at dereference sites, and I3 at deallocation sites; the paper specifies that I5 must occur before I3 at deallocation sites to avoid misreporting double-free. Pointers that are not risky themselves but carry spatial metadata receive I1 at definition and I2 on arithmetic or container updates.
The runtime checks are deliberately simple. At dereference or arithmetic sites, the spatial check verifies nullness, whether Offset <= InitializedLength, and whether Offset <= Capacity. Exceeding initialized length is reported as use-before-initialization; exceeding capacity is reported as out-of-bounds. At dereference or deallocation sites, the temporal check consults temporal metadata to determine whether the pointer is dangling. Dereferencing a dangling pointer is reported as UAF, and deallocating a dangling pointer is reported as double-free.
5. Compiler integration and implementation pipeline
LiteRSan is implemented on top of LLVM-14 and uses a customized rustc 1.64 nightly (Xia et al., 19 Sep 2025). The compiler modifications extend MIR-to-LLVM lowering and specifically alter codegen-ssa and codegen-llvm so that custom LLVM metadata is emitted for raw-pointer-related instructions.
The paper describes the overall pipeline as:
- Rust source to MIR
- MIR lowered to LLVM IR with custom raw-pointer annotations
- LiteRSan static analysis
- Selective instrumentation
- Optimized compilation
- Runtime checking
A notable implementation detail is the decision to insert instrumentation before optimization so that metadata is not lost, while still compiling the final binary with -O3 for realistic performance evaluation. This establishes LiteRSan as a compiler-integrated sanitizer rather than a source-level transformation or an external binary rewrite.
Relative to ASan-retrofitting systems, this implementation strategy is closely aligned with Rust semantics. The system does not merely suppress a subset of ASan checks; it introduces a Rust-specific analysis and metadata model at the compiler level. This suggests that LiteRSan’s reduction in overhead is tied to early semantic filtering rather than only to a more efficient runtime.
6. Evaluation, overheads, and bug-detection behavior
The evaluation uses 28 benchmarks: 26 crates from crates.io and 2 real applications, servo and ripgrep. Each benchmark is run 20 times, and mean overheads are reported (Xia et al., 19 Sep 2025). The paper compares LiteRSan against ERASan and RustSan, and also introduces Semi-LiteRSan, which reuses LiteRSan’s risky-pointer identification but retains ASan runtime checks in order to isolate the contribution of LiteRSan’s lightweight metadata.
| Measure | LiteRSan | Comparator values |
|---|---|---|
| Runtime overhead | 18.84% | ERASan 152.05%; RustSan 183.50%; Semi-LiteRSan 70.04% |
| Memory overhead | 0.81% | ERASan 739.27%; RustSan 861.98%; Semi-LiteRSan 443.90% |
| Compilation overhead | 97.21% | ERASan 1635.35%; RustSan 1193.31% |
The paper reports geometric mean runtime overheads of 18.84% for LiteRSan, 152.05% for ERASan, and 183.50% for RustSan. It reports geometric mean memory overheads of 0.81%, 739.27%, and 861.98%, respectively. Compilation overheads are 97.21% for LiteRSan, 1635.35% for ERASan, and 1193.31% for RustSan. The corresponding reductions attributed to LiteRSan are 87.61% versus ERASan and 89.73% versus RustSan in runtime overhead, 94.06% versus ERASan and 91.85% versus RustSan in compile-time overhead, with the paper attributing the memory reduction mainly to avoiding ASan shadow memory and red zones. It also notes that ERASan and RustSan both timed out on servo and hit a segmentation fault on ripgrep, while LiteRSan completed successfully.
Bug-detection results are reported on RustSec vulnerabilities. LiteRSan detects all 20 most recent bugs in the main table and 55 vulnerabilities total when combined with older cases in the appendix: 21 UAF, 3 double-free, 21 OOB, 7 use-before-initialization, and 3 null-pointer dereference. The paper states that LiteRSan detects all of them with 100% accuracy in the evaluation. ASan-based tools miss four bugs in the reported set: two out-of-bounds bugs, one use-before-initialization bug, and one use-after-free bug.
Two case studies illustrate the claimed difference in detection model. In vm-memory, a slice-like abstraction allows an internal pointer to refer outside a logical region while remaining within a larger allocated object. ASan misses this because red zones surround object boundaries rather than logical sub-bounds, whereas LiteRSan catches it through per-pointer tracking of capacity, initialized length, and offset. In tracing, a vulnerability involving mem::forget leaves pointers logically dangling without a conventional heap-free event. ASan misses the bug because no heap-free updates shadow memory; LiteRSan catches it because ownership state is tracked, and once the owner is forgotten or dropped, associated pointers are marked dangling.
The Semi-LiteRSan ablation is especially informative. Its geometric mean runtime overhead is 70.04%, and its memory overhead is 443.90%. The paper uses this to argue that the final gains come from two sources in combination: precise risky-pointer identification reduces unnecessary instrumentation, and the lightweight metadata-based runtime further cuts runtime and memory cost.
7. Scope, limitations, and relation to prior sanitization models
LiteRSan is explicitly limited to memory safety. The paper states that it does not cover type-conversion bugs such as transmute, cross-language memory bugs originating in C/C++ libraries behind FFI, or non-memory-safety bugs such as races and logic errors (Xia et al., 19 Sep 2025). It is therefore not a full semantic correctness checker.
Within its intended scope, the paper’s central claim is architectural. LiteRSan replaces generic alias analysis and heavyweight ASan machinery with Rust-specific static analysis plus compact per-pointer metadata. Its key innovation is the observation that Rust ownership and lifetimes already eliminate a large number of memory-access risks, so sanitization should focus on the remaining risky pointers rather than instrumenting memory broadly. This leads to a different relationship between compiler semantics and runtime checking than in ASan-based designs.
A common misconception is that Rust’s memory-safety guarantees make sanitization largely unnecessary. The paper rejects that view by focusing on unsafe code and by showing that ownership-aware temporal bugs and logical spatial violations remain detectable targets. Another misconception is that selective instrumentation in Rust can be obtained simply by suppressing some ASan checks. LiteRSan’s comparison with ERASan and RustSan indicates a stronger claim: the decisive difference is not only how many checks are emitted, but whether the analysis reasons in terms of Rust-visible ownership transfers, lifetime structure, and exposed raw-pointer use.
In that sense, LiteRSan is best understood as a Rust-aware sanitizer that reasons like Rust rather than like C.