---
title: 'BRL: Branch Landing for RISC-V CFI'
url: https://www.emergentmind.com/topics/branch-landing-brl
type: topic
---

# BRL: Branch Landing for RISC-V CFI

Branch Landing (BRL) is a landing-based forward-edge control-flow integrity (CFI) framework for RISC-V that enforces source-aware authorization at indirect control-flow targets by replacing fixed-capacity tag checks with Bloom filter membership queries. It introduces two lightweight ISA extensions, `bld` and `brl`, together with a dedicated architectural state register, `BRState`, so that each protected landing site can validate whether the incoming source Section Identifier (SID) belongs to its statically authorized source set with fixed-probe latency independent of the number of authorized sources under a chosen filter configuration [2604.23331].

## 1. Security problem and design objective

BRL is designed for forward-edge control-flow hijacking, especially Jump-Oriented Programming (JOP), function-pointer corruption, virtual function call hijacking, and dispatcher-based gadget chains such as jump tables and computed gotos [2604.23331]. Its starting point is what the paper characterizes as a source-authorization gap in existing forward-edge mechanisms.

Coarse landing-only schemes ensure that indirect branches land on marked landing instructions, but they do not constrain which call site may jump to a given landing site. Type-based CFI narrows the target set by function signature, yet all call sites with the same signature remain in one equivalence class. Tag-based hardware CFI is more expressive, but a fixed-width register limits how many legitimate sources can be represented simultaneously for a target. BRL targets all three limitations at once: per-target source-domain authorization, no fixed-capacity bound on the number of authorized sources, and constant-time hardware checks whose latency does not scale with the size of the authorized-source set [2604.23331].

The mechanism is explicitly forward-edge only. Backward edges still require a separate defense, such as a shadow stack. Within that scope, BRL aims to guarantee that every indirect transfer reaching a protected landing site is preceded by a legitimate `bld`, carries a source SID that belongs to the target’s statically computed authorization set, and consumes that authorization state exactly once [2604.23331].

## 2. Architectural model and enforcement semantics

BRL partitions code into sections and assigns each section a Section Identifier. The section granularity is configurable: function-level, basic-block-level, coarser module-level groupings, or custom groupings via annotations are all supported by the same mechanism [2604.23331]. This configurability is central, because it lets BRL encode policies ranging from type-based authorization to CFG-derived authorization without changing the ISA interface.

Authorization state is propagated through a dedicated CSR, `BRState`, with two fields: `BRState.valid` and `BRState.sid`. The SID field uses 31 bits, so up to $2^{31}$ distinct sections can be represented. At user level, only `bld` and `brl` update `BRState`; ordinary stores and generic CSR writes cannot modify it, modulo privileged operations [2604.23331].

The source-side instruction is `bld SID_{\mathrm{src}}`. It is inserted immediately before each indirect branch and atomically writes the source SID into `BRState.sid` while setting `BRState.valid = 1`. The target-side instruction is `brl SID_T`, placed at each protected landing site. Its behavior is: check `BRState.valid`; if the bit is clear, raise a control-flow protection fault; otherwise interpret `SID_T` as an index into read-only metadata, fetch the Bloom filter descriptor for the landing site, test membership of `BRState.sid`, and either continue execution after clearing `BRState.valid` or raise a control-flow protection fault if membership fails [2604.23331].

This single-use semantics is security-critical. A valid source authorization cannot be replayed across multiple transfers, and a direct jump into a protected landing site without a preceding `bld` fails because `BRState.valid == 0` at `brl` execution [2604.23331]. The threat model further assumes arbitrary memory write capability by the adversary, but excludes modification of executable code pages, BRL metadata in `.rodata`, and direct user-level manipulation of `BRState` [2604.23331].

## 3. Bloom filters, authorization sets, and policy expressiveness

For each landing site $T$, BRL defines an authorized source set
$$
\mathrm{AllowedSources}(T)=\{SID_{\mathrm{src}} \mid \text{policy says src can jump to } T\}.
$$
Instead of storing that set explicitly, BRL encodes it as a per-target Bloom filter in read-only memory [2604.23331]. The target-side `brl` instruction performs a membership query rather than a tag-equality check. This is the main design departure from prior hardware CFI based on fixed-size tag registers.

The Bloom filter parameters are the usual $(m,k,n)$, with false-positive probability
$$
p_{\mathrm{fp}} \approx \left(1-e^{-kn/m}\right)^k.
$$
BRL uses double hashing,
$$
h_i(SID)=(h_1(SID)+i\cdot h_2(SID)) \bmod m,\quad i=0,\dots,k-1,
$$
so only two base hashes are computed and the remaining probes are derived [2604.23331]. The paper states that no false negatives occur, while false positives remain possible and are tuned to be very rare. In the implementation, a fixed filter size is chosen to keep $p_{\mathrm{fp}} < 10^{-3}$ across benchmarks, and no false positives were observed in evaluation [2604.23331].

The same machinery supports multiple policy regimes. At function-level granularity, BRL-Func assigns one SID per function and uses type-based authorization. At basic-block-level granularity, BRL-CFG assigns one SID per basic block and derives `AllowedSources(T)` from the CFG, thereby shrinking equivalence classes when functions share the same signature but are not all legitimate sources for a target [2604.23331].

| Configuration | Policy basis | Aggregate outcome |
|---|---|---|
| BRL-Func | Function-level SIDs, type-based authorization | Mean runtime overhead 0.210% under `brl3`; mean `.text` growth 0.46%; average equivalence class size 1.96 |
| BRL-CFG | Basic-block-level SIDs, CFG-derived authorization | Mean runtime overhead 0.421% under `brl3`; mean `.text` growth 0.52%; average equivalence class size 1.32 |

Across 81 benchmarks, BRL-CFG reduces the average equivalence class size by 32.5% relative to BRL-Func, while the maximum equivalence class remains 9 in both cases [2604.23331]. This suggests that BRL’s main gain is not merely target marking, but a finer source-domain restriction under the same hardware mechanism.

## 4. Compiler pipeline, metadata layout, and system integration

BRL is implemented in the LLVM RISC-V backend and evaluated in the Spike ISA simulator extended with `bld` and `brl` [2604.23331]. The compiler identifies indirect branch sources and landing sites, assigns SIDs according to the chosen granularity, constructs `AllowedSources(T)` either from function signatures or from interprocedural CFG and pointer analysis, emits Bloom filters and descriptors in `.rodata`, and inserts `bld SID_{\mathrm{src}}` before each indirect branch and `brl SID_T` at each protected landing site [2604.23331].

The ABI remains otherwise unchanged. Indirect calls are still `jalr`, returns are still `jalr x0, ra, 0`, and BRL simply decorates forward-edge transfers with source and landing instrumentation [2604.23331]. This matters because BRL is meant as a focused enforcement mechanism rather than a full redesign of the call/return convention.

The metadata layout consists of Bloom filter bit arrays plus a descriptor table indexed by target SID. Each descriptor supplies at least the base address and size $m$ of the corresponding filter. The paper places both descriptors and filters in read-only sections protected by OS page permissions [2604.23331]. On the operating-system side, BRL requires text and `.rodata` to remain read-only, `BRState` to be saved and restored across context switches, and a policy for clearing or preserving `BRState` across traps and interrupts. Clearing on traps is described as fail-closed behavior [2604.23331].

The current prototype assumes whole-program compilation and does not fully address dynamic linking or JIT-generated code. Supporting those cases would require runtime SID assignment and dynamic metadata generation or update [2604.23331]. For out-of-order cores, the paper notes that `bld` should update `BRState` only at commit, and `brl` should consume it at commit as well, so that speculative instructions squashed from the reorder buffer do not violate single-use semantics. A full RTL design is left for future work [2604.23331].

## 5. Evaluation and empirical characteristics

The evaluation uses 81 BEEBS benchmarks compiled with the LLVM RISC-V backend at `-O2` and executed on Spike under a weighted-cycle model where ALU operations and `bld` cost 1 cycle, taken branches cost 2 cycles, load/store cost 3 cycles, `ecall` costs 10 cycles, and `brl` is modeled at 3, 5, or 10 cycles for `brl3`, `brl5`, and `brl10`, respectively [2604.23331].

Under the `brl3` model, mean runtime overhead is 0.210% for BRL-Func and 0.421% for BRL-CFG; medians are 0.013% and 0.014%, and maxima are 3.752% and 10.743%, respectively [2604.23331]. Under `brl5`, mean overhead rises to 0.331% and 0.651%; under the conservative `brl10` model, to 0.633% and 1.228% [2604.23331]. The notable outlier is `cover` under BRL-CFG, with 10.74% at `brl3`, 16.12% at `brl5`, and 29.57% at `brl10`, attributed in the paper to three large switch tables and many CFG-protected targets [2604.23331].

Code size growth in `.text` is also small: BRL-Func has mean 0.46% and median 0.50%, while BRL-CFG has mean 0.52% and median 0.37% [2604.23331]. Metadata overhead appears mainly in `.rodata`: Bloom filters and descriptor tables average about 86 bytes per benchmark for BRL-Func, and BRL-CFG has median metadata size around 264 bytes, with mean 4.15% of total binary size skewed by `cover` [2604.23331].

The security-side evaluation reports zero fail events across all 81 benchmarks, both policy configurations, and all `brl` latency models: every legitimate execution path passed all `brl` checks [2604.23331]. BRL-CFG also protects more targets on average than BRL-Func, with mean protected targets 4.0 versus 2.3, reflecting finer-grained instrumentation [2604.23331]. The paper further states that no false positives were observed across all benchmarks [2604.23331].

Compared with Bratter, a tag-based hardware CFI scheme discussed in the paper, BRL-CFG has much lower mean overhead than the reported 5.99% for Bratter’s combined enforcement and less than half of Bratter’s reported code size overhead of about 1.20% [2604.23331]. The paper attributes this to the shift from fixed-slot tag equality to set membership in memory-backed Bloom filters.

## 6. Position in the CFI landscape, limitations, and implications

BRL occupies a specific point in the forward-edge CFI design space. Relative to CET/IBT-like landing-marker schemes, it is source-aware rather than merely landing-aware. Relative to type-based systems such as FineIBT, Clang CFI, and LLVM-CFI, it can reproduce type-based authorization through BRL-Func while also supporting CFG-derived restrictions through BRL-CFG under the same ISA mechanism. Relative to tag-based hardware CFI such as Bratter, it removes the hard limit imposed by a small tag register and replaces equality against one of a few slots with membership in an arbitrarily large authorized-source set whose capacity scales with filter size in memory [2604.23331].

The paper characterizes BRL as the first RISC-V proposal to combine source-aware authorization, arbitrary-size authorized-source sets, constant-time verification via Bloom filters, and policy agnosticism between type-based and CFG-derived authorization [2604.23331]. A plausible implication is that BRL’s main conceptual contribution is not only low overhead, but a reformulation of hardware CFI checking as set membership rather than tag matching.

Its limitations remain explicit. BRL protects only forward edges; it assumes integrity of code pages and metadata in `.rodata`; Bloom filters admit false positives in principle; it does not address microarchitectural side channels; it relies on correct OS handling of `BRState`; and its current prototype does not support dynamic linking, shared libraries, or JIT-generated code [2604.23331]. The paper therefore frames future work around backward-edge protection, richer policy generators, runtime SID allocation for dynamic code, and cycle-accurate RTL implementations with descriptor caches and optimized hash units [2604.23331].

Within those assumptions, BRL presents a hardware-software co-designed model of forward-edge CFI in which authorization is attached to landing sites, parameterized by compiler-chosen code sections, and enforced through Bloom filter membership queries with fixed probe count. That design makes source precision configurable, keeps the ISA surface minimal, and avoids the fixed-capacity bottleneck that constrains prior tag-based schemes [2604.23331].

Source: https://www.emergentmind.com/topics/branch-landing-brl