1
KS-CFA: Control-Flow Attestation via Symbolic Replay Against Control-Flow Bending Attacks Zhanyu Sha, Konstantinos Markantonakis, Carlton Shepherd, Amir Rafi
arXiv:2607.07926v1 [cs.CR] 8 Jul 2026
✦
Abstract—Control-flow attestation (CFA) enables a remote entity to verify program execution on a target device by monitoring control-flow behaviour at runtime. However, control-flow bending (CFB) attacks remain difficult to detect, where an adversary steers execution along legal edges of the program’s control-flow graph by corrupting branch flags, loop counters, and other runtime data. Existing solutions impose significant drawbacks: they require enumerating vast measurement spaces, cover only a reduced subset of attacks, or rely on low-level hardware modifications. In this work, we present KS-CFA, a new CFA scheme that detects CFB attacks across four transfer types (indirect calls, conditional and indirect jumps, and returns) without those costs. To this end, we combine symbolic execution and selective identification of input-sourced control-flow dependent variables: a strict subset of control-flow-relevant state whose values are directly read from external input. The proving device records, inside a trusted execution environment (TEE), a controlflow trace and the external inputs that determine relevant run-time variables. The verifier then replays the reported path through single-path symbolic execution, predicting each transfer and localising divergences that signal an attack. We implement and evaluate KS-CFA using the RISC-V Keystone TEE and Embench-IoT on QEMU and a Rocket-based FPGA platform (NiteFury II). Prover-side overhead relative to unattested execution ranges from 6.8–20.5× on QEMU and 6.7–32.2× on the FPGA, and verification requires no path or value enumeration. Index Terms—Control-flow attestation, trusted execution environments, symbolic execution, embedded systems, remote attestation, RISC-V
1
I NTRODUCTION
C
ONTROL - FLOW attacks remain a persistent and powerful threat to system security. Traditional control-flow attacks (e.g., code injection, code reuse) tamper with control data (e.g., return addresses, function pointers) to redirect execution through memory-corruption vulnerabilities. Such attacks have been mitigated by control-flow integrity (CFI) techniques, which enforce that a program’s runtime execution follows its control-flow graph (CFG). CFI operates locally on the device and cannot provide remote, cryptographically verifiable evidence of execution correctness. This gap has motivated the emergence of control-flow attestation (CFA) [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12], [13], [14], [15], [16], [17], [18], [19], [20], [21], [22], [23], [24], [25], [26], which enables a remote verifier (V ) to validate
Z. Sha, K. Markantonakis and A. Rafi are with the Information Security Group, Royal Holloway, University of London, Egham, Surrey, United Kingdom. Corresponding author: Z. Sha (e-mail: [email protected]) C. Shepherd is with the Department of Computer Science, Durham University, Durham, UK, and was supported by the UK Engineering and Physical Sciences Research Council through grant EP/Y030168/1.
a program’s execution path against attestation reports sent from the prover device (P ). However, CFA does not detect all control-flow attacks. A particularly subtle class are control-flow bending (CFB) attacks [27], [28], which are fundamentally harder to detect than traditional control-flow threats, such as return-oriented programming (ROP) [29]. In a CFB attack, the adversary manipulates control- and non-control data—branch flags, loop counters, indirect-call targets, or return addresses stored in writable memory—to steer execution along edges that are legal in the CFG, but produce malicious behaviour collectively (e.g., bypassing security checks or escalating privileges). Because individual control-flow transfers respect the CFG, detecting such attacks requires reasoning about whether the runtime values that drove control-flow decisions were themselves legitimate. Few CFA proposals reason about these values at all; those that do provide only partial CFB coverage or do so at substantial cost. DO-RA [2] extends the conventional approach of hash-based attestation—where cumulative hashes are computed at successive nodes of a target program’s CFG (see C-FLAT [1])—by folding the values of runtime variables into the digest. This obligates V to pre-enumerate the set of all legitimate hashes, which is intractable when control flow depends on continuous or high-cardinality inputs. BDFCFA [21] instead requires V to supply the program’s inputs, which presumes V knows the complete space of program inputs and fails when inputs originate from unpredictable sources (e.g., sensor feeds, system state). Geden et al. [8] monitors the legality of individual control-flow transfers in hardware, requiring bespoke CPU pipeline extensions that report only a violation flag rather than detailed traces. Other schemes cover CFB only partially: OAT [13] and ARI [26] monitor control-flow-dependent variables at runtime, but only a subset of them, yielding partial coverage within each transfer type, while LiteHAX [3] cannot distinguish a bending attack from a legitimate access within the same memory allocation. In this paper, we present KS-CFA, a CFA scheme that closes the gap between CFB coverage and the usual costs of attaining it. Our design rests on two ideas. The first is selective input recording. In an offline phase, V analyses the target program at the LLVM intermediate representation (IR) level and classifies every value as either verifier-known (determinable from code, constants, and compile-time facts) or verifier-unknown (dependent on runtime inputs or en-
2
vironment state). Among verifier-unknown values, those that are directly read from external input and that influence control-flow decisions (e.g., branch conditions, loop bounds, call/jump targets), either directly or through data dependencies, are called input-sourced control-flow dependent (ISCFD) variables. At runtime, when an ISCFD variable is assigned by an external input, P records the data read at that assignment and includes it in the attestation report. The second idea is single-path symbolic replay. Rather than pre-computing hashes for every CFG path combination, V validates P ’s attestation report—a sequence of control-flow transfers—by replaying the reported path. During replay, V propagates known values symbolically and substitutes the recorded inputs at the corresponding ISCFD assignments. V checks that each branch decision is semantically consistent with the reported transfer. These two mechanisms together let V combine program semantics with inputs in order to identify semantic infeasibility where the reported successor diverges from V ’s prediction. We thus contribute a CFA proposal that achieves wide CFB coverage without V -side path or value enumeration. KS-CFA covers four exploitable transfer types (indirect calls, conditional jumps, indirect jumps, and returns) and requires a standard trusted execution environment (TEE) without additional hardware. We implement KS-CFA on a RISC-V platform using the Keystone TEE and evaluate it under both QEMU and a NiteFury II FPGA board. The remainder of this paper is organised as follows. §2 covers background and related work. §3 defines the threat model and assumptions. §4 and §5 present the system overview and detailed design, respectively. §6 describes the implementation, §7 evaluates overhead and verification effectiveness, and §8 presents a security analysis. §9 discusses limitations and future work before concluding in §10.
2
BACKGROUND AND R ELATED W ORK
We introduce control-flow integrity, attestation, and bending attacks, and identify the limitations of existing work. 2.1
Control-Flow Integrity and Attestation
CFI [30] enforces that every runtime control-flow transfer respects a predefined policy, typically expressed as a CFG. A CFG is a directed graph G = (V, E) whose vertices v ∈ V represent basic blocks (BBLs) and whose edges (vi , vj ) ∈ E represent permitted transfers. A program satisfies CFI if every runtime transfer follows an edge in E . CFGs are derived by static analysis of source code, binaries, or an intermediate representation such as LLVM IR, or by dynamic analysis of observed test executions. We denote a control-flow path as the sequence of BBLs visited in a single run, T = (v1 , . . . , vn ), equivalently the edge list {(v1 , v2 ), . . . , (vn−1 , vn )}. CFA [20] addresses CFI’s local-only limitation by producing an authenticated record of control-flow behaviour. A common design pattern involves V issuing a challenge to P , which then captures control-flow events during execution. P hashes control-flow events cumulatively, returning a report that is authenticated by a trust anchor. V validates and verifies the report using a pre-computed
Fig. 1. A program’s CFG and two CFB attacks. TN is the benign (unprivileged) path; Tc1 = (B1 , B2 , B4 , B5 ) and Tc2 = (B1 , B3 , B4 , B5 ) are compromised paths whose edges are CFG-legal. (Derived from [31]).
database of legitimate hashes derived from the program’s CFG, flagging any deviations. CFA proposals have varied, firstly, around what is measured: cumulative hashes at CFG nodes (C-FLAT [1], Atrium [5]); explicit edge or path sequences (ReCFA [16], OAT [13], BLAST [17]); and measurements augmented with data values (DO-RA [2], ARI [26]) or memory-access metadata (LiteHAX [3]). Secondly, proposals vary according to how V validates the report: hash matching against a pre-computed database (CFLAT [1], DO-RA [2]); CFG-membership over an explicit trace (ReCFA [16], OAT [13]); value-based checks on instrumented variables (OAT [13], ARI [26]); or learned models over trace embeddings (RAGE [15]). Proposals also vary by trust anchor, such as TEEs (ARM TrustZone [1], [2], [13] and Intel SGX [9], [21]) or dedicated co-processors and on-chip extensions [3], [6], [8], [12]. The reader is referred to Sha et al. [20] for a survey of CFA schemes. 2.2
Control-Flow Bending Attacks
In a CFB attack, the adversary corrupts non-control data (branch flags, loop counters) or control data (function pointers, return addresses) to produce a legal but malicious path. Such attacks have been demonstrated against real-world production software (e.g., CVE-2013-2028 [27]). Fig. 1 gives an example program that reads a password, sets a Boolean flag, and branches on it. CFG G1 has vertices V = {B1 , . . . , B6 } and edges E = {(B1 , B2 ), (B1 , B3 ), . . . , (B4 , B6 )}. Under benign execution with an incorrect password, the program follows TN = (B1 , B3 , B4 , B6 ) and performs unprivileged operations. Two CFB attacks can achieve privilege escalation: 1) Password overwrite. The attacker is able to exploit a vulnerability in B1 to read the hard-coded constant "XXXX" and overwrite the password variable, forcing path Tc1 = (B1 , B2 , B4 , B5 ).
3
2) Flag tampering. The attacker is able to corrupt the admin variable after B3 executes, forcing path Tc2 = (B1 , B3 , B4 , B5 ). Every edge in both Tc1 and Tc2 appears in G1 . A dynamic CFG constructed from comprehensive test inputs may include Tc1 —since it coincides with the legitimate path when the correct password is supplied—but could exclude Tc2 if no test input causes B3 to be followed by B5 . Generally, when a CFB attack follows a path that is reachable under some benign input, no CFG-based check—static or dynamic—can detect it. Detecting such attacks requires reasoning about runtime data beyond the legality of edges. 2.3
CFB Detection Approaches
Traditional CFA schemes such as C-FLAT [1], LO-FAT [12], and CHASE [6] use CFGs to detect control-flow attacks but do not reason about control-flow data. CFB attacks are not detected with static CFGs because the paths are CFG-legal (although call-return matching can still detect CFB attacks on returns [1]). A dynamic CFG detects CFB attacks on returns because a corrupted return is not an observed path. Nevertheless, only attacks on unobserved paths are caught, missing those that coincide with legitimate executions. To address this, OAT [13] uses value-based checking on control-flow dependent variables, but restricts protection to developer-annotated code regions. ARI [26] uses dataflow protection for critical variables in targeted regions and reports their values to V , achieving broader but still incomplete coverage. LiteHAX [3] reports runtime memoryaccess metadata (addresses of loads, stores, and the memory locations they touch) to V , enabling detection of CFB attacks arising from memory corruptions across distinct data objects. However, when the corruption occurs in the same allocation as the overflowed buffer (e.g., fields of the same struct), address-level checks cannot distinguish between corruption and legitimate accesses. RAGE [15] applies graph neural networks to control-flow traces, reporting F1-scores of 98.03% for ROP and 91.01% for data-oriented programming (DOP) attacks on Embench-IoT. Detection is probabilistic due to the learning paradigm; a model trained on benign traces cannot reliably distinguish CFB attacks whose paths resemble legitimate executions (e.g., Tc1 in Fig 1). In the CFI literature, context-sensitive schemes refine the set of legal targets for each indirect control-flow transfer. For such a given transfer, the equivalence class (EC) is the set of target BBLs that the mechanism cannot distinguish using only the static CFG; a smaller EC implies stronger enforcement. PITTYPAT [32], µCFI [33], CFI-LB [34], and OS-CFI [35] reduce EC sizes for indirect calls and returns (typically protecting returns via shadow stacks) but do not achieve singleton ECs for all indirect control-flow transfers, providing only partial protection. BCI-CFI [36] addresses indirect jumps but does not cover conditional branches. vCFI [37] follows a maximalist approach by protecting all control-flow dependent variables through shadow memory-based integrity checking; however, it does not support remote attestation. DO-RA [2] extends attestation measurements to encompass the runtime values of controlflow dependent variables and data-flow dependencies. Offline, V locates branches and loops, identifies constraining
TABLE 1 Comparative summary of related CFI ( shaded rows ) and CFA (unshaded) schemes.
Scheme
Input type
A1
A2
A3
A4
PathArmor [39] PITTYPAT [32] µCFI [33] CFI-LB [34] OS-CFI [35] BCI-CFI [36] vCFI [37]
Binary LLVM LLVM LLVM LLVM LLVM LLVM
G # G # G # G # G # G #
G #
✗ ✗ ✗ ✗ ✗ G #
C-FLAT [1] LiteHAX [3] OAT [13] ARI [26] Geden et al. [8] DO-RA [2] BDFCFA [21]
Binary Source LLVM LLVM Binary Binary Binary
✗ G # # G # G
✗ G # G # G #
KS-CFA
LLVM
# G # G
Traceable
Deps.
✗ ✗ ✗ ✗ ✗ ✗
— — — — — — —
Intel LBR Intel PT Intel PT Intel TSX Intel TSX SW SW
✗ # G # G # G
✗ ▲ ▲ ▲ ▲ ✗ ✗
TrustZone Custom CPU HW TrustZone TrustZone Custom CPU HW TrustZone Intel SGX TEE
A1: CFB-ICALL; A2: CFB-RET; A3: CFB-IJMP; A4: CFB-CJMP. G # Partial coverage; Full coverage; ✗ No coverage. ▲: (OAT [13], ARI [26]: marked regions only; Geden et al. [8]: violation flag and last executed instruction; LiteHAX [3]: control-flow transfers reported as runtime addresses, but load/stores only as running hashes). — Not applicable: CFI enforces locally and produces no report for V . Deps. = platform dependencies; HW = hardware; SW = software-based.
variables, and rewrites the binary with stubs that record transfers and variable values. At runtime, P hashes the recorded sequence, and V checks the result against a preconstructed database of legitimate hashes. Because the hash embeds exact runtime values, each distinct value produces a different digest. When control-flow depends on continuous input(s), V must pre-enumerate a large set of legitimate digests, which is intractable for programs reliant on highcardinality or continuous-valued inputs. BDFCFA [21] incorporates expected input values by recording hashes of legal paths with the input ranges that produce them. P reports a path hash; V validates it against inputs that V itself supplied. This model requires V to be knowledgeable of all program inputs, which lacks scalability when inputs originate from unpredictable sources (e.g., configuration files, system variables, sensor feeds). Geden et al. [8] uses a dedicated hardware module in the CPU pipeline that checks control-flow dependent variables. It performs local attestation, reporting violation flags with limited diagnostic information (e.g., the last executed instruction), limiting V -side attack traceability. SABRE [38] performs CFG-based path verification followed by automatic root-cause analysis and binary patching for buffer overflow and use-after-free vulnerabilities. Tables 1 and 2 summarise our comparison. Few schemes detect CFB attacks across four exploitable transfer types: indirect calls (CFB-ICALL), conditional direct jumps (CFBCJMP), indirect jumps (CFB-IJMP), and returns (CFB-RET) (defined in §3). Existing CFB-aware schemes reveal a tension between attack detection coverage and deployability. Hash-based schemes require V to pre-enumerate the set of legitimate measurement values during the offline phase, which lacks scalability. Dedicated hardware approaches [6], [8] obviate software overhead but require custom low-level modifications. KS-CFA addresses this gap by coupling selective recording of salient inputs that influence control-flow decisions (§5.1), a subset of all program inputs, with single-
4
TABLE 2 Comparison of CFA schemes with comprehensive CFB detection.
Scheme DO-RA [2] BDFCFA [21] Geden et al. [8] KS-CFA
RT Addr
Var Vals
Ext Inp
Viol Flags
Vars Prot
Preenum
Ded HW
■ ■ □ ■
■ □ □ □
□ ■ □ ★
□ □ ■ □
□ □ ✣ □
■ ■ □ □
□ □ ■ □
■: Property present; □: Absent. ✣: All control-flow dependent variables (V.1); ★: Limited to control-flow-influencing inputs that V does not supply (a strict subset of all program inputs). RT Addr = runtime addresses in measurement; Var Vals = variable values; Ext Inp = external inputs; Viol Flags = attacks reported as violation flags without execution trace; Vars Prot = variables protected at runtime; Pre-enum = V must pre-enumerate legitimate measurements; Ded HW = dedicated hardware required.
path symbolic replay (§5.2). This combination gives wide CFB coverage across the four transfer types and provides detailed attack traceability. It avoids V -side enumeration of legitimate paths, the requirement that V be the sole source of program inputs, and dependencies on dedicated hardware beyond a standard TEE.
3
T HREAT M ODEL & A SSUMPTIONS
In this section, we discuss the threat model and assumptions of our proposed scheme. 3.1
Attacker Model
We assume a software adversary who can exploit memorycorruption vulnerabilities (e.g., buffer overflows, use-afterfree) to perform arbitrary reads and writes in the application’s address space, including stack, heap, and global data. The attacker possesses full knowledge of the program’s source code and CFG, and can approximate the dynamic CFG by observing sufficiently many test executions, enabling CFB attacks. Further, we assume that the target system enforces write-xor-execute (W⊕X) memory protection, where writable memory is non-executable and executable memory is non-writable. Consequently, the attacker cannot introduce new instructions when this is enabled; any register manipulation must use instructions already present in the program text. This is a standard assumption in the CFI and CFA literature [1], [2], [3], [6], [16], [20], [40]. V is assumed to be trusted and possesses the program’s intended source code. V receives an authenticated copy of the target program in the offline phase, and the binary is securely deployed on P . We rely on a GlobalPlatform-style TEE with secure and non-secure worlds separated with hardware-enforced isolation such that an attacker cannot read or modify the state of secure world-resident software from the untrusted world [41]. The following are out of scope: attacks targeting the TEE itself (e.g., microarchitectural side channels, fault injection), physical attacks, denialof-service, and data-only attacks (e.g., DOP attacks [42]) that do not change the control-flow path. 3.2
Targeted Attack Classes
Control-flow attacks exploit transfers whose concrete target can vary at runtime. Following prior systematisa-
tions [20], KS-CFA targets the four transfer types that can be weaponised for CFB attacks: • CFB-ICALL. The adversary corrupts a function pointer or virtual-table entry so that an indirect call (icall) reaches an unintended yet CFG-legal callee. • CFB-CJMP. The adversary manipulates a branch predicate or loop counter to flip the outcome of a conditional direct jump (cjmp) or alter iteration counts, steering execution down a legal but unintended path. • CFB-IJMP. The adversary tampers with an index into a jump table (or another pointer used by an indirect branch/jump (ijmp), redirecting execution to a sibling BBL without violating the CFG. • CFB-RET. The adversary overwrites a saved return address so a callee returns (ret) to a different but CFGlegal call site, breaking call-return correspondence. Unconditional direct jumps (ujmp) and direct calls (dcall) have exactly one statically encoded destination; no attacker-controlled data determines the edge, so neither CFB nor traditional control-flow attacks can leverage them. KSCFA also detects traditional control-flow attacks, such as ROP [29] and call- (COP) [43] and jump-oriented programming (JOP) [44], which produce transfers absent from the CFG, as these are strictly easier to identify than CFB attacks.
4
S YSTEM OVERVIEW
We now describe our solution. Fig. 2 illustrates the highlevel architecture of KS-CFA, which follows two phases: an offline phase (compilation and instrumentation) and an online phase (attestation and verification). In an offline phase, V compiles the target program to a RISC-V binary through the LLVM toolchain. During compilation it performs static analysis and instrumentation on LLVM IR and assembly files 1 – 7 before producing the executable binary to be deployed on P in 8 . Static analysis determines, for every BBL, which variable values can be resolved from the source code alone and which depend on runtime inputs. The results are stored in Variable Resolution Tables (VRTs) that V later consults during verification. After compilation, binary analysis 9 constructs an Addr-BBL mapper that translates runtime addresses and branch outcomes back to LLVM IR BBL identifiers. The final executable is deployed on P 10 . The online phase is at the point of execution on P . After the target program runs and terminates, P transmits the captured control-flow path and input log to V . The AddrBBL mapper receives the path 11 and translates it into a sequence of LLVM IR BBL identifiers 12 . V performs abstract execution 13 using the BBL sequence and the input log: a step-by-step symbolic replay of the reported path, querying the VRTs 14 to update a runtime memory model 15 and invoking a predictor to determine the legitimate successor at control-flow transfers 16 – 17 . A mismatch between the predicted and reported successor signals an attack. Fig. 3 illustrates P ’s system architecture. The target program and a trampoline execute in the non-secure world; a measurement engine executes inside a secure enclave. When V initiates attestation, P begins executing the target program. At every designated control-flow transfer, the trampoline intercepts execution and forwards the transfer information
5
LLVM Compilation Pipeline Instrumentation
①
② LLVM IR
Source Code
Static ③ LLVM IR ④ Analysis Analysis
Input Record
⑥
⑤ Assembly Code
Runtime Analyser ⑭ Query
Runtime Memory Variable Model Resolution ⑮ Update Tables ⑯ Request ⑰ Next BBL
⑫ BBL IDs
Predictor
Flow
Measurement
⑦ Assembly Analysis
⑧ Binary Analysis
⑨
Addr-BBL Mapper
⑪
Executable Binary ⑩ Prover
⑬ Inputs
Prover components
Output
Verifier components (online phase)
Verifier components (offline phase)
Fig. 2. Overview of KS-CFA. (Circled steps are described in the text).
Prover Non-secure World Request
Target Program
Verifier
Trampoline Attestation report
Secure World
Measurement Engine
Security Monitor TEE-enabled Hardware
Fig. 3. High-level architecture. Grey indicates components introduced by KS-CFA; green and red show trusted and untrusted areas respectively.
to the measurement engine in the TEE, which appends it to a control-flow log. When an assignment brings an external input that V has not supplied into a variable whose value directly or indirectly influences a control-flow decision (§5.1), the trampoline forwards the data read at that assignment to the measurement engine, which appends it to an input log. After termination, the signed logs are sealed into an attestation report and transmitted to V . The transport scheme is orthogonal to KS-CFA: any remote-attestation protocol ensuring authenticity, integrity, and freshness of the report contents suffices (e.g., [45], [46], [47]).
5
KS-CFA D ESIGN
The central idea is to enable CFA without pre-enumerating legal paths. Instead of building a CFG or hashing all valid traces offline, V replays the single path reported by P , using
pre-computed program semantics to predict what a legitimate execution should do at each control-flow decision. When V can resolve the relevant variables using program semantics, it predicts the successor directly and catches any deviation. When it cannot—because the decision depends on program inputs—V has those inputs (either provided by V or recorded by P in the input log) and uses them to resolve the relevant variables. We present the design in three parts: the ISCFD variable concept that determines V ’s knowledge boundary (§5.1), the abstract-execution procedure that is the core verification mechanism (§5.2), and the measurement infrastructure that captures and translates P ’s control-flow path (§5.3). 5.1
ISCFD Variables and Input Recording
KS-CFA requires program inputs because not all predictions can succeed through program semantics alone. However, V does not need every program input, only those that influence control-flow decisions. The following conditions identify the target variables: [V.1]: Its value can directly or indirectly (e.g., through data dependency) affect a control-flow transfer (e.g. a branch predicate, a loop counter, or a function pointer), [V.2]: Its value is data-dependent on external input—e.g., user input, environment variables, files, sockets, system calls, or return values from third-party libraries—rather than being determined by program constants alone. [V.2a]: A strict subset of [V.2]. The variable has at least one assignment that takes its value directly from an external-input read. A variable that satisfies [V.1] and [V.2] is an inputdependent control-flow dependent (ICFD) variable. A variable that satisfies [V.1] and [V.2a], a strict subset of ICFD, is called an input-sourced control-flow dependent (ISCFD) variable. KS-CFA records the inputs read at ISCFD assignments; the broader ICFD values are reconstructed from these
6
int a = user_input(); // external input int b = a + 3; // data-flow dependent on a bool c; int d = user_input(); // independent input if (b > 0) { c = true; } else { c = false; } if (c) { /* ... */ } else { /* ... */ }
Fig. 5. Example of concrete evaluation.
Var.
[V.1]
[V.2]
[V.2a]
ICFD
ISCFD
a b c d
✓ ✓ ✓ ✗
✓ ✓ ✗ ✓
✓ ✗ ✗ ✓
✓ ✓ ✗ ✗
✓ ✗ ✗ ✗
✓ = satisfied;
✗ = not satisfied.
Fig. 4. Motivating example. The table reports whether [V.1], [V.2], and [V.2a] hold for a variable, and whether it is ICFD or ISCFD.
during replay. Fig. 4 sketches an example. Offline, each direct external-input assignment of an ISCFD variable is marked according to whether the input is supplied by V or recorded by P at runtime. Depending on the deployment scenario, V may supply all, some, or none of the program inputs; the V -versus-P choice of each input depends on the specific scenario. Other assignments to the variable, from constants or computation, are not V ’s concern at runtime; V derives those values from program semantics during replay. Prior work such as BDFCFA [21] requires V to supply all program inputs. KS-CFA imposes no such requirement: V needs only the inputs that influence control-flow transfers, whether V supplies them or P records them at runtime. Recording instrumentation is inserted at each direct external-input assignment whose input is sourced from P . At runtime, the trampoline forwards the value to the measurement engine inside the TEE, which appends it to an input log carried in the attestation report. After V receives the report, V resolves the relevant operands using program semantics or these inputs (provided by V or recorded by P in the input log). KS-CFA avoids prover-side variable protection entirely. Prior work such as OAT [13] (within annotated regions) and vCFI [37] protect all variables satisfying [V.1], incurring overhead proportional to how frequently those variables are defined and used. KS-CFA’s input recording, in contrast, is assignment-based: only the direct external-input assignments whose input is sourced from P trigger recording. It targets only ISCFD variables ([V.1] and [V.2a]), a strict subset of the variables targeted by OAT and vCFI.
5.2
%a = alloca i32, align 4 store i32 10, ptr %a, align 4 %0 = load i32, ptr %a ; -> 10 %1 = add i32 %0, 1 ; -> 11 %2 = load i32, ptr %b ; -> load(%b) %3 = add i32 %2, 2 ; -> add(load(%b), 2)
Single-Path Abstract Execution
Abstract execution is KS-CFA’s core verification mechanism. Given a path reported by P , V replays it symbolically, BBL by BBL, predicting the legitimate successor at every control-flow transfer and comparing it with the reported one. This section describes the offline static analysis that pre-computes the replay data (§5.2.1), the runtime analyser that performs the replay (§5.2.2), and the predictor (§5.2.3).
5.2.1
Static Analysis and VRT Construction
Static analysis (step 2 ) runs once per program in the offline phase. It analyses every BBL in the LLVM IR and produces VRTs that cache the information the runtime analyser would otherwise have to re-derive from raw LLVM IR on every attestation. For each BBL, the static analyser extracts three categories of information. Abstract values of local definitions. The analyser scans each BBL top-to-bottom, evaluating every SSA definition. Within a single BBL, it resolves expressions to concrete values when possible and records symbolic expressions when values depend on definitions from other BBLs. Consider the example in Fig. 5. Definitions %0 and %1 are resolved concretely because %a is allocated and written within the same BBL. Definition %2 depends on %b, which originates in another BBL, so the analyser records the symbolic expression load(%b); %3 is recorded analogously. These symbolic expressions are evaluated during abstract execution once the runtime analyser has accumulated state across multiple BBLs. Expressions that do not involve external input are resolved to concrete values, while those that depend on external input remain symbolic; expressions involving the values of ISCFD variables are resolved by the runtime analyser. Values from external input (e.g. scanf) receive a distinguished unknown marker. For a direct external-input assignment of an ISCFD variable, the marker also carries the input source, so the runtime analyser can substitute the concrete value from V supplied inputs or P ’s input log during abstract execution (§5.2.2). Global variables are identified with their initialisers but deferred to the runtime analyser, since their values depend on the sequence of previously executed BBLs. Memory operations. The analyser records every memorymodifying statement (e.g., store, memcpy, memset) per BBL, in execution order, so the runtime analyser can replay them. Within the single-BBL scope, it also emulates these operations to evaluate the abstract values that depend on them; this emulated memory state is used only for that evaluation and is not retained. Some of these statements, however, have symbolic pointer operands that the analyser cannot resolve within a single-BBL scope. For example, an SSA computation such as getelementptr with a symbolic index produces a symbolic address; the subsequent store using that address cannot be resolved. The store is still recorded, but flagged as a deferred memory operation: the runtime analyser evaluates the index from the runtime memory model and replays the operation. Subsequent SSA definitions or memory-modifying statements that depend on a deferred memory operation are assigned a temporary marker during static analysis. These are replaced by a concrete value or a symbolic expression after the runtime analyser replays the operation.
7
Control-flow metadata. For each BBL, the analyser records: (1) the terminator instruction (br, switch, indirectbr, call/invoke, ret) and, for multi-successor terminators, the relevant operands; (2) any intra-program call instructions, including callee, arguments, result name, and return type; and (3) the program entry BBL. A preliminary LLVM IR pass splits BBLs after every intra-program call, ensuring at most one such call per BBL. 5.2.2 Runtime Analyser Online, the runtime analyser receives the VRTs, and a control-flow path T = ⟨B1 , . . . , BK ⟩ expressed as LLVM IR BBL identifiers, and two chronological input lists, V I and P I . V I holds the inputs V supplied to P ; P I holds the inputs P captured at the instrumented assignments during execution. These cover every external input the program read on the executed path. Algorithm 1 gives the full procedure. The analyser performs abstract execution, which is a singlepath symbolic replay of the reported trace. Unlike classical symbolic execution, it does not explore multiple paths; it follows exactly the sequence reported by P . The analyser maintains the runtime memory model (RMM) comprising: • A call-frame stack (envStack): the analyser pushes a frame on function entry and pops one on return; • A per-frame environment (env): maps local SSA names to their current abstract values; • An abstract memory (mem): stores the contents of objects reached via LLVM pointers. For each BBL, the analyser updates env using the precomputed abstract values from the VRT, resolving any symbolic expressions whose operands have since become concrete. It then replays the memory operations to update mem. SSA definitions within a BBL never depend on memory writes performed in the same BBL, because any load not resolved within the BBL was captured as a symbolic expression during static analysis and is evaluated through mem during the replay phase. When the RA encounters a direct external-input assignment to an ISCFD variable, it reads the source attribution recorded by the static analyser, pops the next entry from VI or PI, and replaces the unknown marker with that value; if the indicated list is empty at that point, an anomaly is reported. Once substitution provides concrete ISCFD values, other LLVM variables whose values depend on them are evaluated. This makes the operands of every control-flow transfer instruction concrete, since each operand’s data-dependence chain terminates at ISCFD variables or at values determined by program semantics. After the update, the analyser invokes the predictor (§5.2.3). Replay proceeds until the call-frame stack empties. If the trace is exhausted before the stack empties, an anomaly is reported. The same applies after replay completes if BBLs remain or either input list is non-empty. 5.2.3 Prediction After updating the RMM for the current BBL, the RA retrieves the control-flow transfer instruction (cfti) and invokes the predictor to determine the expected successor. The predictor’s behaviour depends on the transfer type: • ujmp / dcall. The target is encoded in the IR; the predictor returns it directly. These transfers are not recorded by P , so no comparison is needed.
Algorithm 1: Online verification procedure Input : T = ⟨B1 , . . . , BK ⟩ (reported trace) VRT (variable-resolution tables) PI (prover-recorded inputs) VI (verifier-supplied inputs) Output: A CCEPTED | A NOMALOUS cur ← pop front(T ) if cur ̸= entry BBL then return A NOMALOUS envStack ← [ frame(cur) ] mem ← ∅ while envStack ̸= ∅ do f ← top(envStack) updateRMM(f.env, mem, VRT[cur], PI, VI) // --- predict successor --cfti ← getCFTI(cur) k ← classify(cfti) if k ∈ {ujmp, dcall} then // fixed target p ← fixedTarget(cfti) else if k = ret then p ← f.returnBBL if T is empty then return A NOMALOUS r ← pop front(T ) if p ̸= r then return A NOMALOUS else // cjmp, ijmp, icall if T is empty then return A NOMALOUS r ← pop front(T ) p ← concreteEval(f.env, mem, cfti) if p ̸= r then return A NOMALOUS // --- maintain call stack --if k ∈ {dcall, icall} then envStack.push(frame(p)) top(envStack).returnBBL ← cur.return else if k = ret then envStack.pop() cur ← p if T ̸= ∅ ∨ V I ̸= ∅ ∨ P I ̸= ∅ then return A NOMALOUS else return A CCEPTED
The predictor retrieves the return BBL stored when the corresponding call frame was pushed, enforcing call–return pairing. A mismatch with the reported trace signals a return-address attack. • cjmp / ijmp / icall. The predictor evaluates the branch condition or target expression over the current RMM. Because V has every control-flow-influencing input value (either supplied by V or recorded in P ’s input log during execution), all relevant operands are concrete and evaluation yields a unique successor; a mismatch with the reported trace signals an attack. • Ret.
Why the predictor’s verdict is sound. The predictor needs the value of every cfti operand. Some are determined by program semantics along the executed path, derivable from constants and the program’s own computation. Others are ICFD variables, whose values depend on external inputs. V closes this gap with its input lists: every external input
8
that influences a control-flow transfer, directly or indirectly, is either V -supplied (carried in VI) or P -recorded (carried in PI). After substitution (§5.2.2), the RA resolves the ICFD variables using these inputs, and every cfti operand becomes concrete. An attacker who wishes to bend controlflow must tamper with a variable that influences a controlflow transfer ([V.1]). Tampering changes the variable’s inmemory value on P and thus alters the control-flow path that P reports. V ’s abstract execution, however, produces its correct value by reconstructing the variable from the inputs and program semantics; V ’s predicted edge therefore differs from the tampered reported edge, and the mismatch is detected as an anomaly. KS-CFA therefore does not require runtime protection on these variables; V ’s abstract execution always recovers their correct values. 5.3
Measurement Infrastructure
The abstract-execution procedure operates on LLVM IR BBL identifiers, but P captures raw runtime information. This section describes how control-flow data is captured, translated, and buffered.
Algorithm 2: Addr-BBL mapper Input : PR (raw control-flow path), addr to mbb Output: LLVM IR BBL sequence T , or V IOLATION
T ← ⟨⟩ foreach c ∈ PR do if IsRuntimeAddr(c) then mbb ← Lookup(addr to mbb, c) if mbb not found then ReportViolation() if c = StartAddr(mbb) then Append(T , IRBBL(mbb)) else if mbb = ENTRY HOOK and c is first in PR then Append(T , IRBBL(mbb) + ".start") else if c is a known post-call address in mbb then Append(T , IRBBL(mbb) + ".return") else ReportViolation() cur ← mbb else // c is a branch bit cur ← DelegateMBB(cur) if TermType(cur) ̸= cjmp then ReportViolation() mbb ← TargetMBB(addr to mbb, c, cur) Append(T , IRBBL(mbb)) cur ← mbb
5.3.1 Instrumentation and Path Format V instruments P ’s binary ( 6 ) to capture four transfer types: cjmp, ijmp, icall, and ret. ujmp and dcall are not recorded because their targets are fixed in the program text and cannot be redirected without violating W⊕X. For ijmp, icall, and ret, the captured data is the target runtime address; for cjmp, a single bit records branch-taken or branch-not-taken, since both targets are known statically. Why runtime addresses, not BBL identifiers. Reporting BBL identifiers would require instrumentation at both the entry and exit of every BBL, because P captures only a subset of transfer types and must still identify which BBL was entered. By contrast, capturing control-flow transfer information at the point of transfer requires instrumentation only at exits with a recorded transfer, yielding fewer capture points per BBL and less time spent in the trampoline per execution. Additionally, runtime addresses detect mid-BBL entry attacks: if an attacker redirects control-flow to an instruction in the interior of a BBL, the captured address will not match any legitimate position, namely a BBL start, a post-call return site, or the entry hook (the capture recording the program’s first executed BBL). This reveals the attack. BBL identifiers, being coarser-grained, would lose this information.
looks it up in addr to mbb, verifies that it corresponds to a BBL start address, and emits the corresponding LLVM IR BBL identifier. Post-call addresses receive a .return suffix to prevent an attacker from conflating a return landing with a normal BBL entry. The entry hook similarly receives a .start suffix to prevent an attacker from conflating the program’s initial entry with a normal entry to the entry BBL, which the program may also reach at runtime. Any address that matches no legitimate position indicates a midBBL entry attack. If c is a branch bit, the mapper follows unconditional control-flow edges (ujmp, fall-throughs, and dcall to functions in the program) from the current machine BBL until it reaches a control-flow divergence. Since branch bits represent only cjmp, the divergence must be a cjmp; reaching any other divergence (ijmp, icall, ret) indicates a control-flow attack. The mapper then emits the successor using the branch bit.
5.3.2 Address-to-BBL Translation The Addr-BBL mapper translates the raw control-flow path into LLVM IR BBL identifiers. It relies on a three-level mapping—LLVM IR BBLs, machine BBLs, and runtime addresses—constructed by three offline analysis passes: 1) LLVM IR analysis ( 3 – 4 ) embeds BBL names as metadata in the assembly output. 2) Assembly analysis ( 7 ) maps each machine BBL to its LLVM IR BBL and records it in an ELF section. 3) Binary analysis ( 9 ) augments the mapping with start/end addresses, post-call addresses, and terminator types, producing a map addr to mbb. Algorithm 2 gives the translation procedure. For each element c in the raw path: if c is a runtime address, the mapper
5.3.3 Batch Logging A world switch per control-flow transfer is computationally expensive; ≈100–1000× slowdown has been reported elsewhere for whole-program attestation [17]. To ameliorate this, KS-CFA uses batch logging, where the trampoline accumulates control-flow data in a shared-memory buffer and triggers a world switch only when the buffer is full or the target program terminates. Because the buffer resides in non-secure memory between flushes, this enables some attack vectors. Buffer writes from program code are blocked by software fault isolation: every store in the instrumented program is preceded by a mask-and-check sequence that rejects writes into the buffer region; the buffer boundaries are stored in read-only pages (using mprotect). Write-pointer
9
corruption is prevented by reserving a dedicated register R2 for the pointer; under W⊕X (§3), only existing trampoline instructions can modify R2 : the pointer is incremented after each log entry and reset only at step (3) of the flush routine below. KS-CFA is flexible in enabling per control-flow transfer measurements without batch logging; we explore the performance overhead in §7.2.2. The remaining attack is one that jumps to the trampoline’s pointer-reset instruction, causing the next batch to overwrite previously logged data. KS-CFA neutralises this with a guard register R1 . The flush routine executes: (1) set R1 ← 1; (2) world-switch to flush the buffer; (3) reset R2 ; (4) check R1 = 1, reporting an attack if not; (5) set R1 ← 0. No other BBL modifies R1 or resets R2 . Jumping to step 2 and 3 is detected at step 4 because R1 was never set. Jumping to step 1 commits the existing buffer contents before resetting the pointer, so no logged data is lost.
6
I MPLEMENTATION
We implement KS-CFA targeting embedded devices to demonstrate its feasibility. V ’s static analysis and compilation pipeline run on an x86-64 host using LLVM 18.1.8 [48], while P executes on RISC-V with Keystone TEE [46]. The codebase comprises approximately 25K LOC. 6.1
Verifier Toolchain V ’s offline phase is implemented as a sequence of LLVM passes integrated into the standard compilation pipeline. A built-in LowerSwitch pass first converts LLVM switch statements to chains of if-else branches, so that every multi-way branch is represented as a series of two-target conditional branches in the IR. Two custom LLVM IR passes follow: one performs LLVM IR-level analysis, identifying BBL names and embedding them as metadata for later stages; the other instruments the ISCFD variables (§5.1) whose values are not provided by V , so that their values are recorded at runtime. A machine-level pass, inserted before the AsmPrinter, instruments assembly code to capture control-flow transfer information at runtime. This stage requires access to hardware-level details (registers, stack layout) unavailable at the IR level. Finally, an AsmPrinter handler emits the LLVM-IR-to-machine-BBL mapping into custom ELF sections, which the binary analyser reads after linking. These ELF sections are not needed at runtime and can be stripped from the deployed binary. In our prototype, ISCFD variables are identified manually using two Clang/GNU-style attributes: __attribute__((annotate("PInput"))) and __attribute__((annotate("VInput"))). They indicate that the variable’s value comes from a nonV source or from V , respectively. The recording pass instruments only variables marked PInput; the static analyser reads both annotations and informs the runtime analyser of each variable’s input source. Recording fires only at assignments where a PInput-annotated variable receives its value directly from an external-input read; assignments from constants or computation do not trigger recording. Every ISCFD variable must be annotated as either PInput or VInput; the choice depends on the specific attestation and target program scenario.
TABLE 3 Backend compiler and linker flags.
Flag
Stage
Purpose
-O0
LLC
-disable-tail-calls
LLC
-fast-isel=false
LLC
-mattr=-relax
LLC
-Wl,--no-relax
Linker
Disable machine-level optimisations Preserve call–return pairing Consistent instruction selection Disable RISC-V relaxation in codegen Disable RISC-V relaxation in linker
KS-CFA’s runtime analyser verifies execution at the LLVM IR BBL level, while P executes machine-level BBLs. This imposes three requirements at the backend: 1) Each LLVM IR BBL maps to exactly one machine BBL. 2) Any machine BBLs introduced by the backend (e.g. from instruction selection or register allocation) must terminate with an ujmp or dcall—transfer types whose targets are fixed and not recorded by P . 3) LLVM IR BBLs whose terminators are recorded transfer types (cjmp, ijmp, icall, ret) must be preserved: the backend must not reorder, merge, or duplicate them. Backend optimisations routinely violate these requirements. For instance, tail-call optimisation replaces a call– return pair with a jump, breaking call–return matching. RISC-V linker relaxation can alter instruction sequences. We thus disable backend optimisations (Table 3). Frontend optimisations (source code to LLVM IR) remain enabled, as they do not affect the IR-to-machine-BBL correspondence. §9 discusses paths toward relaxing this constraint.
6.2
Prover Implementation
P comprises a trampoline in the non-secure world and a measurement engine inside the Keystone secure enclave. The standard Keystone SDK uses event-driven communication: the enclave issues an OCALL to switch to the non-secure world, which invokes a dispatch function and automatically returns to the enclave when the function completes. We modify the OCALL dispatcher so that it continues targetprogram execution after the dispatch function returns rather than switching back to the enclave. The trampoline then writes control-flow data and input records to a sharedmemory buffer and triggers world switches explicitly, either when the buffer is full (batch logging) or at program termination. This modification affects only non-secure-world code and does not alter the trusted computing base (the security monitor and secure kernel remain unchanged).
7
E VALUATION
This section presents the evaluation of KS-CFA, discussing the approach and results for prover- and verifier-side performance using FPGA- and QEMU-based implementations.
10
TABLE 4 Control-flow transfer distribution across benchmarks.
TABLE 5 Runtime overhead on FPGA (NiteFury II, 100 MHz). All times in ticks; Slow. = slowdown vs. baseline.
Program
ujmp
cjmp
ijmp
dcall
icall
ret
MT* (%)
mont64 crc32 MAT md5 minver N-AES N-SHA NSICH st SMATE
1 102 3 079 19 788 6 415 293 1 902 398 131 2 018 30
910 1 027 9 725 4 371 222 1 001 65 626 709 53
0 0 0 0 0 0 0 0 0 0
22 1 028 805 9 13 13 7 2 610 13
0 0 0 0 0 0 3 0 0 0
22 1 028 805 9 13 13 10 2 610 13
45.3 33.4 33.8 40.5 43.4 34.6 16.2 82.5 33.4 60.6
S-MACH
41
32
20
0
0
0
55.9
* A multi-target (MT) transfer has >1 potential target BBL.
7.1
Experimental Setup
We evaluate KS-CFA using the Embench-IoT suite [49] using the Keystone TEE on a NiteFury II FPGA with a single-core Rocket CPU (RV64IMAFDC, 100 MHz, 16 KB L1 instruction and data caches), and QEMU 7.2.1 emulating RISC-V under Ubuntu 24.04 on an Intel Ultra 9 275HX, with 21 GB RAM. Because Embench-IoT programs use fixed inputs for deterministic scoring, we modified each program so that a single variable reads its value directly from an external input, chosen by manual inspection to suit the program’s semantics. Because the same modification is applied uniformly across the suite, whether a program’s controlflow becomes input-dependent reflects its own structure rather than program selection; each program therefore has at most one ISCFD variable (§5.1). We also add two custom programs: state-machine, a state-machine unit test containing ijmp transfers (absent in the benchmark suite), and state-machine-P, a variant augmented with I/O operations to increase per-BBL execution time (§7.2.1). Selection was also bounded by manual analyst effort in annotating ISCFD variables for the prototype, and identifying ICFD variables (§5.1). In the experiments, V supplies all such inputs. The program set spans control-flow density, ICFDto-CFD ratio, and program structure (cryptographic transforms, numerical computations, and state-driven dispatch).1 Table 4 summarises the control-flow distributions. 7.2
Prover-Side Overhead
We measure runtime in two configurations: (1) unprotected baseline, and (2) measurement (control-flow capture + batch logging). Under the experimental setting, configuration (2) reports the measured prover-side overhead. All times exclude fixed-cost initialisation (enclave creation and trampoline setup: ≈1.07 M FPGA ticks, ≈1.17 M QEMU ticks) and finalisation (enclave destruction: ≈9 K FPGA ticks, ≈399 K QEMU ticks). Tables 5 and 6, and Fig. 6, report execution times and overall slowdown. Across EmbenchIoT, this ranges from 2.7×–18.4× excluding the final TA invocation and 6.7–32.2× including it on FPGA; the corresponding QEMU ranges are 3.8–13.5× and 6.8–20.5×. 1. Scale factors (LOCAL SCALE FACTOR, CPU MHZ) were set to 1 so that each program executes its core computation once.
Program
Baseline
mont64 crc32 MAT md5 minver N-AES N-SHA NSICH st SMATE S-MACH* S-MACH-P* *
Measurement only Excl. TA
Slow. (×)
Incl. TA
Slow. (×)
23 73 511 118 11 135 23 31 66 7
206 1 117 2 598 833 92 360 74 563 731 58
9.0 15.3 5.1 7.1 8.1 2.7 3.2 18.4 11.0 8.3
576 1 893 5 388 1 866 268 902 421 895 1 286 226
25.1 25.9 10.5 15.8 23.5 6.7 18.0 29.3 19.4 32.2
2 617
33 682
13.6 1.1
365 907
152 1.5
Custom program; not part of Embench-IoT. Excl. TA = program execution only; Incl. TA = plus final TA processing of remaining buffer; Slowdowns are computed from unrounded tick counts. TABLE 6 Runtime overhead on QEMU. All times in ticks.
Program
Baseline
mont64 crc32 MAT md5 minver N-AES N-SHA NSICH st SMATE S-MACH
Measurement only Excl. TA
Slow. (×)
Incl. TA
Slow. (×)
2 104 910 3 076 2 852 5 346 5 795 6 619 30 632 2 524 4 123
13 091 6 460 16 179 16 748 25 713 29 773 24 963 412 530 13 147 32 463
6.2 7.1 5.3 5.9 4.8 5.1 3.8 13.5 5.2 7.9
22 380 18 610 37 938 28 311 36 211 48 255 45 607 424 174 26 134 42 802
10.6 20.5 12.3 9.9 6.8 8.3 6.9 13.9 10.4 10.4
890
5 172
5.8
22 739
25.5
Notation as in Table 5. Slowdowns are computed from unrounded tick counts and rounded for readability.
S-MACH sits well outside these ranges on FPGA (13.6× excl. TA, 152× incl. TA) and at the high end on QEMU (5.8× excl. TA, 25.5× incl. TA); we attribute this to its very low per-BBL computation cost and analyse it separately in §7.2.1. In KSCFA, after the target program finishes, an additional world switch is required to flush the remaining contents of the log buffer and invoke the TA to process them. As a result, there are two possible measurement endpoints: (1) program execution only, excluding the final TA processing; and (2) execution plus the final TA processing. The second naturally yields a higher overhead. 7.2.1
Effect of Control-Flow Density
Runtime slowdown is dominated by control-flow density: the ratio of transfer events to useful computation per BBL. Programs whose BBLs contain only a few arithmetic instructions (e.g. S-MACH, ≈2 ticks baseline) spend most of their instrumented runtime on capture overhead, yielding extreme slowdown factors. To isolate this effect we created
11
Slowdown (×, log scale)
500
10×
7×
100 50
No batch (per-transfer) KS-CFA (batch log)
14× 12×
6×
5× 7×
7×
4×
2×
10 5 1
4 2 T 5 er S A H nt6 crc3 MA md minv N-AE N-SH NSIC o m
MAT crc32 md5 st S-MACH-P N-AES NSICH mont64 N-SHA S-MACH minver SMATE
st ATE SM
11× 26× 16× 19× 1.5× 7× 29× 25× 18× 152× 24×
100
(a) Batch vs. no-batch slowdown on FPGA (inc. final TA processing). Numbers above ‘no batch’ (red) are relative to ‘batch log’ (blue).
Baseline Measurement
32×
101
102
104
103
Execution time (ticks)
(b) Average overhead across all benchmarks, showing baseline execution, measurement overhead.
Fig. 6. Overhead results for KS-CFA on the test programs.
110
Concrete-evaluation success on MT transfers (%)
TABLE 7 Overhead without batch logging (per-transfer world switch, including final TA). All times in ticks.
6 programs: crc32, MAT, md5, N-AES, N-SHA, st
100 minver
90 80 70
mont64
No ICFD vars Partial (< 60%) Dominant (> 60%) Linear fit (r = -0.99)
50 40 30
mont64 crc32 MAT md5 minver N-AES N-SHA NSICH st SMATE
S-MACH
60
0
20
40
FPGA
Program
SMATE
NSICH
60
ICFD / CFD ratio (%)
80
100
QEMU
Time
Slow. (×)
Time
Slow. (×)
5 981 13 353 65 138 27 001 1 685 6 648 894 4 212 8 532 801
260 182 127 229 148 49 38 138 129 114
482 443 999 322 4 774 969 2 028 336 153 781 531 813 86 909 475 781 662 006 79 226
229 1 098 1 552 711 29 92 13 16 262 19
Slow. = slowdown vs. unprotected baseline. Program abbreviations as in Table 4.
Fig. 7. Concrete-evaluation success on multi-target transfers.
S-MACH-P, a variant of S-MACH that adds printf calls to increase per-BBL execution time and tested it on the FPGA board as primary platform. Slowdown dropped from 152× to 1.5× (measurement, incl. final TA) confirming that overhead scales with control-flow density rather than with program size. The general pattern holds across all benchmarks: higher control-flow density, and a larger proportion of multi-target transfers all correlate with higher overhead. 7.2.2 Batch Logging Table 7 reports overhead when every control-flow transfer triggers an immediate world switch to the TA (i.e., batch logging disabled). Slowdown ranges from 38–260× on FPGA and 13–1 552× in QEMU, consistent with BLAST’s reproduction of C-FLAT and OAT overhead (100–1 000×) [17].
On the FPGA platform, one complete measurement cycle— capture, world switch to the enclave, TA processing, and return—averages ∼7 ticks. By comparison, BLAST [17] reports ARM TrustZone world-switch times across CFA schemes of 45 µs (OAT on HiKey), ∼40 µs (C-FLAT on Raspberry Pi 2), and 190 µs (BLAST on Raspberry Pi 3). Although different hardware prevents a direct comparison, the structural difference is clear: batch logging reduces the number of switches by orders of magnitude.
7.3
Verification Effectiveness
We analyse the case in which V must reconstruct the controlflow path from program semantics without program inputs. Table 8 reports the proportion of multi-target transfers V resolves through concrete evaluation.
12
TABLE 8 Concrete evaluation vs. unresolved transfers. All transfers where the predictor must resolve a multi-target branch.
TABLE 9 Per-program ISCFD, ICFD, and CFD variable counts.
Program
cjmp conc.
cjmp unres.
ijmp
icall
ret
Total conc.
Total unres.
C/U (%)
C/U MT (%)
Program
ISCFD
ICFD
CFDa
ICFD/ CFD (%)
mont64 crc32 MAT md5 minver N-AES N-SHA NSICH st SMATE
458 1 027 9 725 4 371 198 1 001 65 254 709 45
452 0 0 0 24 0 0 372 0 8
— — — — — — — — — —
— — — — — — 3 — — —
22 1 028 805 9 13 13 10 2 610 13
1 604 6 162 31 123 10 804 517 2 929 483 389 3 948 101
452 0 0 0 24 0 0 372 0 8
78.0 100 100 100 95.6 100 100 51.1 100 92.7
51.5 100 100 100 89.8 100 100 40.8 100 87.9
S-MACH*
14
18
20
—
0
75
18
80.7
65.4
mont64 crc32 MAT md5 minver N-AES N-SHA NSICH st SMATE
1 0 0 0 1 0 0 1 0 1
42 0 0 0 8 0 0 254 0 28
48 3 7 18 26 24 16 259 6 87
87.5 0 0 0 30.8 0 0 98.1 0 32.2
S-MACHb
1
2
4
50.0
*
Custom program; not part of Embench-IoT. conc. = resolved by concrete evaluation; unres. = not resolved by concrete evaluation; C/U = concrete / (concrete + unresolved); MT = multi-target transfers only; — = transfer type absent in program. Columns for ujmp and dcall are omitted (always resolved concretely by definition). ijmp, icall, and ret (where present) are all resolved by concrete evaluation; unresolved transfers are exclusively cjmp. Program abbreviations as in Table 4.
7.3.1
a
CFD = control-flow dependent variables (satisfying requirement V.1 only; §5.1). b Custom program; not part of Embench-IoT. Program abbreviations as in Table 4.
Concrete Evaluation Success
6/11 programs (crc32, MAT, md5, N-AES, N-SHA, st) achieve 100% concrete evaluation on multi-target transfers (cjmp, ijmp, icall, ret), independent of V ’s input knowledge. Their control-flow is determined entirely by compiletime constants, so abstract execution yields the unique legal control-flow path without offline enumeration. Most hashbased CFA schemes (e.g., C-FLAT [1], BDFCFA [21]) do not face measurement explosion for such programs, since their execution does not depend on external inputs, and thus only one legal control-flow path exists. The scalability advantage of KS-CFA is more pronounced for the input-dependent programs discussed in §7.4. Among the five other programs, evaluation success on multi-target transfers ranges from 40.8% (NSICH) to 87.9% (SMATE). Even without input knowledge, V ’s abstract execution uses compile-time constants, call-site parameters, and the abstract memory model to resolve a substantial fraction of multi-target transfers. RET in particular always succeeds: the prediction depends on the call site (tracked by V from reported transfers in RA) rather than input values. The success rate of concrete evaluation depends on how external inputs influence control-flow transfers in the program and on the taken path. Static analysis alone cannot determine the exact rate, but the ICFD-to-CFD ratio (Table 9)—the share of control-flow-dependent variables that are input-dependent—serves as a useful predictor. When external inputs influence a larger share of control-flow dependent variables, the operands of control-flow transfers are more likely to depend on those inputs and therefore to remain symbolic during abstract execution. Table 9 reports this data. Six programs have zero ICFD variables (external inputs do not influence their control-flow), while NSICH has 254 ICFD variables out of 259 CFD variables (98%). These counts reflect the single external input per program (§7.1): the one ISCFD variable is the recorded source, and the broader ICFD set comprises the control-flow-relevant variables that may derive from it under static data-dependence analysis. ISCFD is thus the small set V records, from which it reconstructs the ICFD values during replay (§5.1). Fig. 7 quantifies the relationship. Concrete-evaluation success on multi-target transfers follows the ICFD/CFD ratio with a strong negative
correlation (r = −0.99), and coverage declines as the ICFD share rises. Even at NSICH’s extreme ICFD share of 98.1%, V resolves 40.8% of multi-target transfers through abstract execution alone. The high 87.5% headline ICFD/CFD ratio of mont64 is offset by frequent loop counters that resolve concretely, so mont64 still achieves 51.5% (Table 8). V ’s evaluation success depends on the input information it receives. Three benchmark programs illustrate this gradient. In md5, control-flow depends not on input content but on input length, which is hardcoded in the program; if the length were not hardcoded, three additional variables would become ICFD and concrete evaluation would no longer reach 100%. Similarly, nettle-aes and nettle-sha256 would require six and two additional ICFD variables respectively if V lacks knowledge of input size. In minver, the function mmul has branches that depend on call-site parameters; because the caller passes hardcoded values, those variables remain non-ICFD and concrete evaluation succeeds. A subtler case arises in the heap-allocation function used by md5 (defined in a shared library file). A pointer derived from heap_ptr + size governs a conditional branch: void *next_heap_ptr = heap_ptr + size; if ((next_heap_ptr % sizeof(void *)) != 0) { ... }
Here, size is a compile-time constant, and heap_ptr is in a statically-defined buffer. V ’s abstract memory model represents heap_ptr symbolically with a known alignment property, so the branch outcome is determined from size alone. This demonstrates that the abstract memory model contributes directly to successful concrete evaluation. These observations show that providing V with as much input information as possible reduces the ICFD variable count and increases concrete-evaluation success. Where supplying inputs is not feasible, hardcoding values in the program reduces the ICFD variable count, thus potentially increasing concrete-evaluation success. If V knows all inputs that influence control-flow transfers, V can reconstruct the unique legal path through concrete evaluation. 7.3.2 Fallback Chain When concrete evaluation cannot resolve a multi-target transfer, V can apply fallback mechanisms specific to the
13
transfer type before trusting the reported edge. For ijmp, V can use the indirectbr candidate list. LLVM IR’s indirectbr instruction enumerates the potential targets of an indirect branch, and the static analyser extracts this list during the offline phase. In abstract execution, when the predictor cannot resolve the transfer due to symbolic operands, V checks whether the reported target appears in the list; a target outside the list signals an attack. For icall, V can use a conventional static CFG, derived offline, to serve as a baseline filter. When concrete evaluation on icall fails, V checks that the reported edge is CFG-legal. RET is not part of the fallback chain because V ’s call-return tracking resolves ret targets regardless of input knowledge. V could further reduce its reliance on the reported edge by incorporating an SMT solver (e.g., Z3) into the predictor. The solver would infer variable constraints from reported branches. With these inferred constraints, the predictor would resolve additional cjmp, ijmp, and icall transfers through concrete evaluation. Unlike ijmp and icall, cjmp has no type-specific fallback check; an unresolved cjmp would rely on SMT constraint inference where available, and would otherwise be trusted as reported. 7.4
Discussion
KS-CFA exhibits ≈7–32× overhead on the Embench-IoT benchmarks for the measurement-only configuration including final TA processing, which is high in absolute terms but must be interpreted against two baselines. First, without batch logging, the same workloads incur ≈38– 1,552× overhead (Table 7), confirming that batch logging is beneficial for TEE-based CFA schemes, consistent with [17]. Second, Embench-IoT programs are computation-intensive microbenchmarks with unusually high control-flow density (few instructions per BBL). In relation to verification strength, KS-CFA achieves 100% concrete evaluation across the test programs when V has access to all control-flowinfluencing inputs. Six programs require no input recording because their control-flow depends only on compile-time constants and yields a single legal path. Although hashbased CFA schemes would also store only one hash for these single-path programs, they cannot determine this without enumerating all possible paths from the CFG and storing the resulting hashes. The scalability advantage is more pronounced for the remaining five programs, whose controlflow depends on external inputs. KS-CFA avoids this tradeoff entirely; concrete evaluation supported by input recording (when V doesn’t supply inputs directly) handles inputdependent programs without offline path enumeration.
8
S ECURITY A NALYSIS
This section evaluates the detection mechanism against the four CFB transfer types and traditional control-flow attacks. We test KS-CFA by reproducing the in-memory state that a successful exploit would induce, isolating the detection logic (similar to [2]). Each test is performed by tampering with target variables at runtime via the gdb debugger in the non-secure world of the Keystone QEMU implementation.2 2. The default Keystone QEMU image does not include gdb in the non-secure world; we modified the build configuration to include it.
The adversary capabilities are consistent with the threat model (§3), i.e., arbitrary read/write in the application’s address space, knowledge of the program’s source code and static CFG, and W⊕X enforcement. The tests are summarised in Table 10 and described below. Attacks [T.1a]–[T.5] tamper control-flow dependent variables whose values are determined by program semantics, validating V ’s abstract-execution path; [T.6] tampers an ICFD variable whose value depends on external input, and V substitutes the upstream ISCFD inputs so that the predicate is resolved through evaluation before the branch is reached. The pair [T.1a] and [T.1b] additionally illustrates that the Addr-BBL mapper and abstract execution provide complementary coverage of traditional control-flow attacks: the former rejects addresses that fail to align with any legitimate BBL position, while the latter rejects edges that connect valid BBLs in ways the CFG forbids. 8.1
Traditional Control-flow Attacks
Conventional ROP [29], JOP [44], and COP [43] attacks redirect execution to addresses that are not valid BBL entry points or that violate the program’s edge structure. The Addr-BBL mapper (Algorithm 2) rejects any reported address that does not match a legitimate position (a BBL start address, a recorded post-call return site, or the entry hook), and the edge-by-edge replay rejects any transition inconsistent with the program’s structure. By experiment, we tamper with a return address in mont64 under two configurations. In [T.1a], the corrupted address lands midBBL, matching neither a BBL start, a recorded post-call return site, nor the entry hook. Thus, the Addr-BBL mapper rejects it outright. In [T.1b], the corrupted address targets a legitimate BBL entry that is nonetheless invalid for this call site: a CFG-illegal edge whose destination coincides with a valid BBL boundary. The mapper accepts the address, but the runtime analyser records the expected return BBL and detects the mismatch during abstract execution. 8.2
CFB-CJMP
In this attack ([T.2]), an adversary corrupts a branch variable or loop counter to alter a conditional branch. Let v be the corrupted variable and Bi the BBL containing the conditional branch. If v is not ICFD—i.e., its value is deterministic from the program semantics—the predictor’s concrete evaluation over the RMM yields a unique expected target Bj . The attacker’s corruption causes P to report a different successor Bk ̸= Bj ; the consistency check is triggered and the trace is flagged ANOMALOUS. If v is ICFD, concrete evaluation requires external input. However, V knows every external input that influences control flow directly or indirectly, either from V ’s side or from P ’s side. Therefore, V is able to replace the ISCFD variable value when assigned by the external inputs, which makes the related prediction successful and yields the unique target BBL. The attacker bends P ’s control flow, but V knows the proper values of controlflow-dependent variables and computes the right target Bj . P therefore reports a different successor Bk ̸= Bj , and the trace is flagged as ANOMALOUS. By experiment, we target mont64’s main loop. We flip the loop bound rpt from 1 to 0, causing the core computation to be skipped. Because rpt is
14
resolvable by concrete evaluation, the predictor computes the legitimate successor and rejects the divergence. 8.3
CFB-ICALL
[T.4] investigates CFB attacks against indirect calls. The attacker overwrites a function pointer or virtual-table entry so that an indirect call dispatches to an unintended yet CFGlegal callee. If the pointer’s value is V -known, the predictor resolves the call target concretely and detects the mismatch. If the pointer is ICFD, V resolves the call target concretely through external input substitution, and the mismatch is detected. N-SHA’s existing icall sites are single-target, so to exercise multi-target indirect dispatch, we extend N-SHA with a second initialisation function that shares the SHA256 context layout but loads a different initial state. The two functions are dispatched through a function-pointer table, yielding a two-target icall site that is CFG-legal under fully precise static CFI; this configuration mirrors common patterns in cryptographic libraries that select between algorithm variants at initialisation. We overwrite the function pointer at the call site; the predictor computes the legitimate target and detects the mismatch. We note that this is a methodological compromise driven by the absence of multitarget icall sites in the Embench-IoT programs (§7.1), but the detection mechanism itself is independent. 8.5
ID
Attack Class
Program
Corruption target
Detection mechanism
[T.1a] [T.1b] [T.2] [T.3] [T.4] [T.5] [T.6]
Trad. Trad. A4 A3 A1 A2 A4
mont64 mont64 mont64 S-MACH N-SHA mont64 minver
Return address Return address Loop bound (rpt) Dispatch index Function pointer Return address Predicate (n)
Addr-BBL mapper Abstract execution Abstract execution Abstract execution Abstract execution Abstract execution Abstract execution
CFB-IJMP
Here, the attacker targets indirect jumps. They may tamper with an index or pointer used by an indirect branch (indirectbr), redirecting execution to a sibling BBL within the same jump table. The analysis is structurally identical to CFB-CJMP: if the index is V -known, concrete evaluation predicts the correct target and a mismatch is detected. If it is ICFD, V uses external inputs to make concrete evaluation succeed and yield the proper successor. In [T.3], we redirect an indirect jump to a different but CFGlegal jump-table target in the S-MACH program, skipping a state’s operations. The predictor resolves the dispatch index concretely and flags the divergence. 8.4
TABLE 10 Attack summary and the associated detection mechanisms.
CFB-RET
The attacker targets return addresses; for example, by overwriting the saved return address on the stack so that the callee returns to a different but CFG-legal caller ([T.5]). KSCFA detects this without monitoring the return address directly. On icall, the trampoline records the dynamically resolved target; dcall targets are statically known to V and need not be recorded. In both cases, at verification time the runtime analyser pushes the expected return BBL (the instruction following the call site, encoded as callBBL.return) onto a shadow call stack on entry to the callee. When a ret is encountered, the predictor pops the shadow stack and compares the expected return BBL against the reported successor. A corrupted return address causes the reported successor to diverge from the shadowstack entry, and the trace is flagged ANOMALOUS. This enforces strict call-return correspondence without requiring a hardware shadow stack. By experiment, we redirect the return address of mulul64—called from multiple sites in
Trad. = traditional CFG-violating control-flow attacks (ROP/JOP/COP). A1–A4 denote CFB attack types from Table 1. Programs as in Table 4.
mont64—to a different but CFG-legal caller’s return site. Each call frame pushes the expected return BBL onto the shadow call stack, and the divergence between expected and reported successor surfaces in abstract execution. 8.6
ICFD Variable Tampering
In this experiment ([T.6]), we tamper with an ICFD predicate in minver_fabs (program minver) to flip the outcome of a conditional branch. Although concrete evaluation of the branch requires external input, V substitutes the upstream ISCFD inputs during abstract execution, and the predicate is resolved through subsequent evaluation before the branch is reached. The predictor computes the unique legitimate successor and rejects the divergence.
9
L IMITATIONS AND F UTURE W ORK
In this section, we discuss the limitations of this work and outline potential mitigations and future directions. 9.1
Prototype Limitations
Our prototype identifies ISCFD variables through manual annotations. A missed annotation leaves an external input unrecorded, creating a potential blind spot. Over-annotated variables, in contrast, incur a small penalty. Such variables are not consulted by the predictor at a control-flow transfer; they carry no security consequence, and cost only extra recorded inputs, or additional V -side storage. Automatically labelling relevant variables would be a fruitful direction of future research. Example approaches may involve taint analysis from external-input sources (e.g., scanf, socket reads, file I/O), and a data-dependence analysis that identifies variables whose values directly or transitively influence control-flow transfers, which we defer to future research. Additionally, the static analyser marks variables that receive return values from system library calls as ISCFD, even when their outputs are deterministic given known parameters. This inflates the ISCFD set and increases P overhead. Extending the analyser to model common library functions would reduce this. The static analysis and abstract execution phases also currently process all LLVM variables, including those with no influence on control-flow transfers. A pre-processing pass that eliminates such variables from the VRTs would reduce offline analysis time and online verification cost. The prototype also targets statically-linked
15
binaries with ASLR disabled, both common simplifications in CFA implementations (e.g., OAT [13]). Finally, reporting runtime addresses, branch bits, and the input log produces longer attestation logs than hashbased schemes that emit one or more compact digests per execution. For long-running programs, this increases communication and storage costs. KS-CFA already elides deterministic edges (ujmp and dcall, which are not recorded); existing orthogonal techniques, e.g., periodic flushing [4] and compression [19], can mitigate this. An evaluation of trace-compression strategies is also deferred to future work.
platform. The concrete evaluation success can reach 100% when V has access to all control-flow-influencing inputs, meaning V reconstructs the unique legal path. Prover-side overhead on the Embench-IoT benchmarks ranges from 6.7× to 32.2× on the FPGA and 6.8× to 20.5× on QEMU. Automating ISCFD identification and adopting binary lifting are the principal directions for future work. More broadly, we demonstrate that selective input handling combined with single-path verification offers a practical design point for CFB resilience in embedded attestation.
9.2
[1]
R EFERENCES Compilation Constraints
The prototype’s runtime analyser verifies execution at LLVM IR BBL granularity, but P executes machine BBLs; backend compiler transformations can break the correspondence between the two. To maintain a faithful mapping, the prototype enforces three compilation constraints (§6), which together necessitate disabling backend optimisations and impose a performance penalty. A promising direction for relaxing this constraint is a binary lifter (e.g., Rellume [50]), which could reconstruct LLVM IR from the final binary, producing an IR whose BBL layout more directly reflects the machine code. RISC-V’s regular instruction encoding is well-suited to lifting; unlike x86, it lacks variable-length instructions and complex multi-operation primitives (e.g., repeated string instructions) that complicate lifters. An evaluation of this approach is left to future work. 9.3
Alternative Deployments
KS-CFA’s verification approach—selective input handling and single-path symbolic replay—is not inherently tied to Keystone. In principle, any CFA scheme whose P reports the targets of control-flow transfers (runtime addresses, branch bits, or equivalent) could integrate KS-CFA’s replay engine as an additional verification layer, though adaptations would be needed to handle different path formats (e.g., machine-level BBL identifiers, or paths that include unconditional transfers). As discussed in §7.3, even when the prover cannot provide program inputs (e.g., for privacy), KS-CFA can still detect CFB attacks on its own. In this case, when KS-CFA is layered on conventional CFA schemes, not all fallback mechanisms described in §7.3.2 are applicable, but static CFG integration is generally available to detect traditional control-flow attacks. For binary-based conventional schemes, KS-CFA could potentially use the binary-lifter approach discussed in §9.2 to obtain the required LLVM IR. Therefore, integrating KS-CFA’s verification engine with these schemes extends detection coverage to CFB attacks, a more sophisticated class of control-flow attack.
10
C ONCLUSION
We presented KS-CFA, a CFA scheme that addresses CFB attacks through the ISCFD variables concept and single-path symbolic replay. It provides detection across four exploitable transfer types without requiring a static CFG, dedicated hardware, or verifier-side path enumeration. We implemented KS-CFA on RISC-V using Keystone and evaluated it using Embench-IoT on both QEMU and NiteFury II FPGA
T. Abera, N. Asokan, L. Davi, J.-E. Ekberg, T. Nyman, A. Paverd, A.-R. Sadeghi, and G. Tsudik, “C-FLAT: Control-flow attestation for embedded systems software,” in ACM Conf. Computer and Communications Security, 2016. [2] B. Kuang, A. Fu, L. Zhou, W. Susilo, and Y. Zhang, “DO-RA: Data-oriented runtime attestation for IoT devices,” Computers & Security, vol. 97, p. 101945, 2020. [3] G. Dessouky, T. Abera, A. Ibrahim, and A.-R. Sadeghi, “LiteHAX: Lightweight hardware-assisted attestation of program execution,” in Int’l Conf. Computer-Aided Design. IEEE, 2018. [4] A. Caulfield, N. Rattanavipanon, and I. D. O. Nunes, “ACFA: Secure runtime auditing and guaranteed device healing via active control flow attestation,” in USENIX Security Symposium, 2023. [5] S. Zeitouni, G. Dessouky, O. Arias, D. Sullivan, A. Ibrahim, Y. Jin, and A.-R. Sadeghi, “Atrium: Runtime attestation resilient under memory attacks,” in Int’l Conf. Computer-Aided Design. IEEE, 2017. [6] G. Dessouky, S. Zeitouni, A. Ibrahim, L. Davi, and A.-R. Sadeghi, “CHASE: A configurable hardware-assisted security extension for real-time systems,” in Int’l Conf. Computer-Aided Design. IEEE, 2019. [7] R. Ben Yehuda, M. Kiperberg, and N. J. Zaidenberg, “Nanovised control flow attestation,” Applied Sciences, vol. 12, no. 5, 2022. [8] M. Geden and K. Rasmussen, “Hardware-assisted remote runtime attestation for critical embedded systems,” in 17th Int’l Conf. Privacy, Security and Trust. IEEE, 2019. [9] M. Morbitzer, B. Kopf, and P. Zieris, “GuaranTEE: Introducing control-flow attestation for trusted execution environments,” in IEEE 16th Int’l Conf. Cloud Computing. IEEE, 2023. [10] D. Huo, Y. Wang, C. Liu, M. Li, Y. Wang, and Z. Xu, “LAPE: A lightweight attestation of program execution scheme for baremetal systems,” in IEEE 22nd Int’l Conf. High Performance Computing and Communications. IEEE, 2020. [11] J. Gonzalez-Gomez, H. Nassar, L. Bauer, and J. Henkel, “LightFAt: Mitigating control-flow explosion via lightweight PMU-based control-flow attestation,” in IEEE Int’l Symposium on Hardware Oriented Security and Trust. IEEE, 2024, pp. 222–226. [12] G. Dessouky, S. Zeitouni, T. Nyman, A. Paverd, L. Davi, P. Koeberl, N. Asokan, and A.-R. Sadeghi, “LO-FAT: Low-overhead control flow attestation in hardware,” in 54th Design Automation Conference, 2017. [13] Z. Sun, B. Feng, L. Lu, and S. Jha, “OAT: Attesting operation integrity of embedded devices,” in IEEE Symposium on Security and Privacy. IEEE, 2020, pp. 1433–1449. [14] M. Conti, E. Dushku, and L. V. Mancini, “RADIS: Remote attestation of distributed IoT services,” in 6th Int’l Conf. Software Defined Systems. IEEE, 2019. [15] M. Chilese, R. Mitev, M. Orenbach, R. Thorburn, A. Atamli, and A.-R. Sadeghi, “One for all and all for one: GNN-based controlflow attestation for embedded devices,” in IEEE Symposium on Security and Privacy. IEEE, 2024. [16] Y. Zhang, X. Liu, C. Sun, D. Zeng, G. Tan, X. Kan, and S. Ma, “ReCFA: Resilient control-flow attestation,” in 37th Annual Computer Security Applications Conference, 2021. [17] N. Yadav and V. Ganapathy, “Whole-program control-flow path attestation,” in ACM Conf. Computer and Communications Security, 2023. [18] I. D. O. Nunes, S. Jakkamsetti, and G. Tsudik, “Tiny-CFA: Minimalistic control-flow attestation using verified proofs of execution,” in Design, Automation and Test in Europe. IEEE, 2021. [19] F. Toffalini, E. Losiouk, A. Biondo, J. Zhou, and M. Conti, “ScaRR: Scalable runtime remote attestation for complex systems,” in 22nd Int’l Symposium on Research in Attacks, Intrusions and Defenses, 2019.
16
[20] Z. Sha, C. Shepherd, A. Rafi, and K. Markantonakis, “Control-flow attestation: Concepts, solutions, and open challenges,” Computers & Security, vol. 150, p. 104254, 2025. [21] H. Li, T. Shen, F. Bai, and B. Gong, “Blockchain-assisted distributed fog computing control flow attestation,” Security and Communication Networks, vol. 2022, no. 1, p. 6128155, 2022. [22] Y. Li, Q. Zhou, B. Li, and Y. Zhuang, “CFRV: A decentralized control-flow attestation schema using mutual secret sharing,” Sensors, vol. 22, no. 16, p. 6044, 2022. [23] A. J. Neto and I. D. O. Nunes, “ISC-FLAT: On the conflict between control flow attestation and real-time operations,” in Real-Time and Embedded Technology and Applications Symposium. IEEE, 2023. [24] J. Liu, Q. Yu, W. Liu, S. Zhao, D. Feng, and W. Luo, “Log-based control flow attestation for embedded devices,” in Int’l Symposium on Cyberspace Safety and Security. Springer, 2019. [25] J. Hu, D. Huo, M. Wang, Y. Wang, Y. Zhang, and Y. Li, “A probability prediction based mutable control-flow attestation scheme on embedded platforms,” in 18th IEEE Int’l Conf. Trust, Security and Privacy in Computing and Communications. IEEE, 2019. [26] J. Wang, Y. Wang, A. Li, Y. Xiao, R. Zhang, W. Lou, Y. T. Hou, and N. Zhang, “ARI: Attestation of real-time mission execution integrity,” in USENIX Security Symposium, 2023. [27] N. Carlini, A. Barresi, M. Payer, D. Wagner, and T. R. Gross, “Control-flow bending: On the effectiveness of control-flow integrity,” in USENIX Security Symposium, 2015. [28] S. Chen, J. Xu, E. C. Sezer, P. Gauriar, and R. K. Iyer, “Non-controldata attacks are realistic threats.” in USENIX Security Symposium, 2005. [29] H. Shacham, “The geometry of innocent flesh on the bone: Returninto-libc without function calls (on the x86),” in ACM Conf. Computer and Communications Security, 2007. [30] M. Abadi, M. Budiu, U. Erlingsson, and J. Ligatti, “Control-flow integrity principles, implementations, and applications,” ACM Trans. Information and System Security, vol. 13, no. 1, 2009. [31] H. B. Debes, E. Dushku, T. Giannetsos, and A. Marandi, “Zekra: Zero-knowledge control-flow attestation,” in ACM Asia Conf. Computer and Communications Security, 2023, pp. 357–371. [32] R. Ding, C. Qian, C. Song, B. Harris, T. Kim, and W. Lee, “Efficient protection of path-sensitive control security,” in USENIX Security Symposium, 2017. [33] H. Hu, C. Qian, C. Yagemann, S. P. H. Chung, W. R. Harris, T. Kim, and W. Lee, “Enforcing unique code target property for control-flow integrity,” in ACM Conf. Computer and Communications Security, 2018. [34] M. Khandaker, A. Naser, W. Liu, Z. Wang, Y. Zhou, and Y. Cheng, “Adaptive call-site sensitive control flow integrity,” in IEEE European Symposium on Security and Privacy. IEEE, 2019. [35] M. R. Khandaker, W. Liu, A. Naser, Z. Wang, and J. Yang, “Originsensitive control flow integrity,” in USENIX Security Symposium, 2019. [36] Y. Wang, Q. Li, Z. Chen, P. Zhang, G. Zhang, and Z. Shi, “BCI-CFI: A context-sensitive control-flow integrity method based on branch correlation integrity,” Information and Software Technology, 2021. [37] D. Jung, M. Kim, J. Jang, and B. B. Kang, “Value-based constraint control flow integrity,” IEEE Access, vol. 8, pp. 50 531–50 542, 2020. [38] A. I. Caulfield, N. Rattanavipanon, and I. D. O. Nunes, “Run-time attestation and auditing: The verifier’s perspective,” in 18th ACM Conf. Security and Privacy in Wireless and Mobile Networks, 2025. [39] V. Van der Veen, D. Andriesse, E. Göktaş, B. Gras, L. Sambuc, A. Slowinska, H. Bos, and C. Giuffrida, “Practical context-sensitive CFI,” in ACM Conf. Computer and Communications Security, 2015. [40] B. Niu and G. Tan, “Modular control-flow integrity,” in ACM Conf. Programming Language Design and Implementation, 2014. [41] C. Shepherd and K. Markantonakis, Trusted Execution Environments. Springer, 2024. [42] H. Hu, S. Shinde, S. Adrian, Z. L. Chua, P. Saxena, and Z. Liang, “Data-oriented programming: On the expressiveness of noncontrol data attacks,” in IEEE Symposium on Security and Privacy. IEEE, 2016, pp. 969–986. [43] E. Göktas, E. Athanasopoulos, H. Bos, and G. Portokalidis, “Out of control: Overcoming control-flow integrity,” in IEEE Symposium on Security and Privacy. IEEE, 2014, pp. 575–589. [44] T. Bletsch, X. Jiang, V. W. Freeh, and Z. Liang, “Jump-oriented programming: A new class of code-reuse attack,” in 6th Symposium on Information, Computer and Communications Security, 2011. [45] C. Weinhold, M. U. Sardar, I. Mihalcea, Y. Deshpande, H. Tschofenig, Y. Sheffer, T. Fossati, and M. Roitzsch, “Separate
but together: Integrating remote attestation into TLS,” in USENIX Annual Technical Conference, 2025. [46] D. Lee, D. Kohlbrenner, S. Shinde, K. Asanović, and D. Song, “Keystone: An open framework for architecting trusted execution environments,” in 15th European Conf. Computer Systems, 2020. [47] W. A. Johnson, S. Ghafoor, and S. Prowell, “A taxonomy and review of remote attestation schemes in embedded systems,” IEEE Access, vol. 9, pp. 142 390–142 410, 2021. [48] C. Lattner and V. Adve, “LLVM: A compilation framework for lifelong program analysis and transformation,” in Int’l Symposium on Code Generation and Optimization. IEEE, 2004. [49] D. Patterson, J. Bennett, M. Bennett, H. Chelin, D. Harris, J. Hellar, W. Jones, K. Moron, P. Savini, R. Shepherd et al., “Embench IoT 2.0 and DSP 1.0: Modern embedded computing benchmarks,” Computer, vol. 58, no. 5, pp. 37–47, 2025. [50] A. Engelke and M. Schulz, “Instrew: Leveraging LLVM for high performance dynamic binary instrumentation,” in 16th ACM Int’l Conf. Virtual Execution Environments, 2020.
Zhanyu Sha (B.Sc., M.Sc.) is currently a Ph.D. student in Information Security at Royal Holloway, University of London. He received his M.Sc. in Cyber Security from King’s College London. He also earned two B.Sc. degrees: one in Information and Computer Science from Xi’an Jiaotong-Liverpool University, China, and another in Computer Science from the University of Liverpool, U.K. His research interests include software security, trusted execution environments, and embedded systems.
Konstantinos Markantonakis (B.Sc., M.Sc., MBA, Ph.D.) is a Professor of Information Security in Royal Holloway University of London, and the Director of the Information Security Group Smart Card and IoT Security Centre (SCC). He obtained his B.Sc. (Lancaster University), M.Sc., Ph.D. (London) and his MBA in International Management from Royal Holloway, University of London. His research interests include smart card security and applications, secure cryptographic protocol design, embedded systems security, autonomous systems and trusted execution environments.
Carlton Shepherd (B.Sc., Ph.D.) received his Ph.D. Information Security from Royal Holloway, University of London, and his B.Sc. in Computer Science from Newcastle University. He is currently Assistant Professor of Cyber Security at Durham University. Previously, he was a Lecturer in Computer Science at Newcastle University, and a Senior Research Fellow at the Information Security Group at Royal Holloway, University of London. His research interests centre around the security of trusted execution environments (TEEs) and their applications, secure CPU design, embedded systems, applied cryptography, and hardware security.
Amir Rafi (B.Sc., M.Sc.) obtained his M.Sc. in Information Security from Royal Holloway, University of London, his B.Sc. in Computing from Queen Mary, University of London, and he is currently completing his Ph.D. in Information Security at Royal Holloway, University of London. He is a member of the Information Security Group Smart Card and IoT Security Centre (SCC) and was previously Research Assistant at the Information Security Group at Royal Holloway, University of London. His research interests include digital rights management, trusted execution environments and embedded systems security.