---
title: Control-Flow Flattening
url: https://www.emergentmind.com/topics/control-flow-flattening
type: topic
---

# Control-Flow Flattening

Searching arXiv for the provided papers and closely related control-flow flattening work.
First, locating the deobfuscation paper by title.
Searching: "Analyzing Chain of Thought (CoT) Approaches in Control Flow Code Deobfuscation Tasks"
Control-flow flattening (CFF) is a code obfuscation technique that restructures a program by decomposing it into basic blocks and routing execution through a centralized dispatcher, typically implemented as a loop with a switch statement, while preserving functional behavior and radically changing the control-flow graph (CFG) [2604.15390][2003.05836]. In practical compiler-style realizations, the original structured control flow of nested conditionals and loops is replaced by a fresh state variable or program counter, and execution repeatedly dispatches to the basic block associated with the current state until a terminal state is reached. Recent work treats CFF both as an obfuscation to be inverted by deobfuscation systems and as a semantics-preserving transformation that can be studied formally with respect to security policies such as constant-time [2604.15390][2003.05836].

## 1. Transformation semantics and operational structure

CFF is described as a transformation on control flow rather than merely a syntactic rewrite. One formulation introduces source code \(\mathcal{C}\), its AST \(\mathcal{S}\), and a state transition graph \(G_{cff}=(V,E)\) for the flattened program, where \(V\) is the set of dispatcher states and \(E \subseteq V \times V \times \Phi\) is a set of directed edges labeled by predicates \(\phi \in \Phi\) [2604.15390]. Each state corresponds roughly to a basic block or switch case, and each transition records which next state is selected under which predicate.

The semantic preservation requirement is expressed by a semantic function
\[
\llbracket \cdot \rrbracket: \mathcal{C} \times \mathcal{X} \to \mathcal{Y},
\]
with the constraint that the original program \(\mathcal{C}\) and the flattened program \(\mathcal{C}_{cff}\) satisfy
\[
\llbracket \mathcal{C} \rrbracket(\mathbf{x}) = \llbracket \mathcal{C}_{cff} \rrbracket(\mathbf{x})
\]
for all inputs, even though their CFGs are radically different [2604.15390]. This combination of semantic invariance and structural disruption is the defining property of CFF.

Operationally, the transformation proceeds by splitting the original function into basic blocks, introducing a dispatcher construct and a fresh state variable \(\sigma\) or `pc`, initializing that variable to the entry state, and replacing direct control-flow edges with assignments to the state variable followed by a return to the dispatcher. In the canonical formulation studied for constant-time preservation, a flattened program has the shape
```pascal
pc := 1;
while (1 ≤ pc) do
  switch pc :
    case 1: ...; pc := n1;
    case 2: ...; pc := n2;
    ...
    case k: ...; pc := 0;
```
where termination occurs when `pc` is set to a special exit value such as `0` [2003.05836]. Informally, nested loops and conditionals are flattened into a centralized dispatcher-driven state machine.

## 2. CFG formalization, structural distortion, and layered obfuscation

For structural analysis, the original and recovered CFGs are modeled as
\[
G_1 = (V_1,E_1), \qquad G_2 = (V_2,E_2),
\]
where \(V\) is the set of basic blocks and \(E \subseteq V \times V\) is the set of control-flow edges [2604.15390]. The paper on LLM-based deobfuscation evaluates recovery quality with Graph Edit Distance (GED) and the normalized distance
\[
d(G_1,G_2)=\frac{\mathrm{GED}(G_1,G_2)}{\max\left(|V_1|+|E_1|,\ |V_2|+|E_2|\right)},
\]
from which it defines the Structural Similarity Score
\[
\mathrm{SRS}(G_1,G_2)=1-d(G_1,G_2),
\]
so that \(\mathrm{SRS}\in[0,1]\), with \(1\) denoting structurally identical CFGs [2604.15390].

CFF dramatically alters graph structure. Many control-flow edges are funneled through the same dispatcher node, and the original structured CFG is replaced by a dispatcher-centered topology. The reported effect is that cyclomatic complexity and graph edit distance between the original and flattened CFGs grow, which directly lowers SRS when comparing original and flattened graphs [2604.15390]. In other words, CFF preserves extensional behavior while maximizing intensional dissimilarity at the CFG level.

The same study places CFF in a broader family of control-flow obfuscations by distinguishing three obfuscation modes:

| Mode | Description |
|---|---|
| Opaque only | Inject bogus conditional branches around real blocks |
| CFF only | Flatten control flow without bogus branches |
| Opaque-CFF | Combine both, with opaque predicates embedded inside flattened control flow |

Opaque predicates are formally defined as predicates whose outcome is fixed but hard to prove statically:
\[
\exists \tau \in \{\top,\bot\}:\ \forall \mathbf{x} \in \mathrm{domain}(p),\ p(\mathbf{x})=\tau
\]
[2604.15390]. In the combined Opaque-CFF setting, the analyst must both reverse the dispatcher and identify which apparent transitions are guarded by invariant predicates whose alternative branches are unreachable. This layered strategy compounds structural and semantic complexity.

## 3. Deobfuscation as inversion of the flattened state machine

In the deobfuscation setting, CFF is treated as an invertible but difficult transformation. The input is obfuscated code \(\mathcal{C}_{obf}\), produced by Tigress or O-LLVM via CFF, Opaque, or Opaque-CFF, and the objective is to synthesize deobfuscated code \(\mathcal{C}_{deobf}\) such that the recovered CFG is close to the original and program behavior is preserved [2604.15390]. For CFF specifically, deobfuscation means removing the dispatcher, switch-based state machine, and related artifacts; reconstructing structured control flow; eliminating useless state variables and dead case blocks; and removing unreachable paths introduced by opaque predicates.

The CoT-guided workflow is encoded as a multi-phase pipeline. It first detects the dispatcher \(\mathcal{D}\) and the state variable \(\sigma\). It then extracts case blocks
\[
\mathcal{B} = \{B_1,\dots,B_n\} \gets Ex\text{-}Cases(\mathcal{D})
\]
and initializes the flattened transition graph
\[
G_{cff}=(V,E)\gets \emptyset.
\]
For each case \(B_i\), it determines the current state label
\[
s_{curr}\gets GetCaseLabel(B_i),
\]
analyzes state updates
\[
\mathcal{T}_i \gets AnalyzeTransitions(B_i,\sigma),
\]
and inserts the corresponding vertices and labeled edges into the transition graph [2604.15390]. This extracts the dispatcher’s state machine.

After transition extraction, the method reconstructs a more natural CFG by detecting back edges \(\mathcal{L}\), performing **TopologicalSort** over \(G_{cff}\) while accounting for loops, and combining blocks in that order [2604.15390]. Opaque predicates are then analyzed by propagating the values of participating variables and retaining only the reachable branch when the predicate is always true or always false. A final cleanup phase removes dead variables, including the dispatcher state variable, removes unreachable code, and normalizes the AST.

The central methodological claim is that Chain-of-Thought prompting improves performance because it asks the model to externalize step-by-step reasoning about dispatcher structure, state transitions, back edges, and invariant predicates, rather than merely rewriting code textually [2604.15390]. Zero-shot prompting, by contrast, is reported to make local edits, miss dispatcher logic, and produce code that fails to compile or diverges semantically.

## 4. Empirical behavior, benchmark results, and failure modes

The evaluation corpus consists of 12 standard C benchmarks: Merge Sort, Heap Sort, Quick Sort, Binary Search, Dijkstra, BFS, DFS, Knapsack, Matrix Multiplication, N-Queens (with solution printing), AVL tree with rotations, and Huffman encoding/decoding [2604.15390]. The first nine are grouped as simpler CFGs, while N-Queens, AVL, and Huffman are grouped as more complex CFGs. Each benchmark is compiled with GCC 4.6 on Debian and then obfuscated using Tigress or O-LLVM under the three obfuscation configurations listed above; identifiers and comments are stripped before LLM inference [2604.15390].

Structural recovery is measured with SRS, and semantic preservation is approximated by executing the original and deobfuscated programs and comparing textual outputs with BLEU [2604.15390]. BLEU is explicitly described as a “simple, reproducible, lightweight lexical baseline” for behavior similarity. The evaluated reasoning-mode LLMs are GPT5, o3, DeepSeek-V2, Qwen-3 MAX, and QWQ-32B [2604.15390].

Among the tested models and by applying CoT, GPT5 achieves the strongest overall performance, with an average gain of about **16%** in control-flow graph reconstruction and about **20.5%** in semantic preservation across the benchmarks compared to zero-shot prompting [2604.15390]. For GPT5 under O-LLVM CFF, zero-shot SRS is 87.0% and CoT SRS is 99.7%, corresponding to +14.6% relative improvement; under Tigress CFF with CoT, GPT5 reaches SRS 100% and BLEU 98.1%, while under O-LLVM CFF with CoT it reaches SRS 99.7% and BLEU 98.5% [2604.15390]. DeepSeek-V2 on Tigress CFF illustrates the role of prompting particularly clearly: zero-shot SRS is 54.5% and CoT SRS is 88.4% (+62.2% relative improvement), while BLEU increases from 59.6% to 94.9% (+59.2%) [2604.15390].

Performance depends not only on obfuscation level and obfuscator choice but also on the intrinsic complexity of the original CFG [2604.15390]. Group 1 programs remain comparatively robust under moderate obfuscation, whereas Group 2 programs degrade significantly as obfuscation intensity rises. Some models, specifically QWQ-32B and o3, fail completely beyond a certain opaque density and are unable to produce executable code [2604.15390]. The reported failure modes include simple omissions such as missing headers or struct definitions, structural hallucinations in which CFG recovery is plausible but type correctness fails, and semantic hallucinations in which compilable code changes behavior. A salient example is a deobfuscated tree routine that computes the sum of subtree heights rather than the maximum, thereby changing a tree-height computation into something closer to node counting for the sample tree [2604.15390].

These results support two distinct conclusions. First, CFF produced by common dispatcher patterns can often be inverted to a high degree by CoT-guided LLMs. Second, high CFG-level recovery does not guarantee semantic correctness, especially for layered obfuscation and complex original graphs [2604.15390].

## 5. Constant-time preservation under canonical flattening

A separate line of work studies whether CFF preserves the constant-time policy rather than how easily it can be reversed [2003.05836]. The security question is: given a program that already satisfies a constant-time side-channel policy, does applying CFG flattening preserve that policy? The answer proved for a canonical flattening scheme is affirmative: every program satisfying the policy still does after the transformation [2003.05836].

The formal setting is a deterministic labeled transition system whose small-step semantics emits leakage traces. The leakage model includes the sequence of evaluated operations in arithmetic and boolean expressions, used as a proxy for timing, and certain control-flow decisions, such as the branch outcomes of conditionals [2003.05836]. Constant-time is expressed as observational non-interference (ONI): for any two attacker-indistinguishable initial configurations, executions must produce identical leakage sequences and the same termination behavior in lockstep. The paper states the condition as
\[
\stepn{A}{t}{n}{B} \land \stepn{A'}{t'}{n}{B'} \land \phi(A,A')
\implies
t=t' \land (B \in S_f \text{ iff } B' \in S_f),
\]
where \(\phi\) captures attacker-visible equivalence on configurations [2003.05836].

The flattening pass is defined recursively with a fresh program counter variable \(\trg{\wvar{pc}}\), a `while` loop guarded by `1 ≤ pc`, and a `switch` encoded as sugared nested conditionals [2003.05836]. A size function \(\size{c}\) determines how many dispatcher cases each command consumes, and a recursive constructor \(\cmdlbl{\wvar{pc}}{c}{n}{m}\) assigns labels to the flattened representation of each command so that `pc := 0` denotes termination. Conditionals and loops preserve their original guards: every `if b then` in the source becomes an `if b then` inside a case in the flattened program, and every `while b do` becomes a case that tests the same predicate and updates `pc` accordingly [2003.05836].

The proof architecture instantiates the secure-compilation framework of Barthe–Grégoire–Laporte based on CT-simulations [2003.05836]. It constructs a general simulation \(\confrel{}{p}{}\) between source and flattened configurations, together with a number-of-steps function and a measure that control stuttering, and then defines a CT-simulation equivalence \(\eqc\) by equality of command syntax. The resulting theorem yields the corollary that control-flow flattening preserves the constant-time policy [2003.05836].

The proof intuition is that flattening does not introduce new secret-dependent guards or memory accesses beyond the original program. The additional `pc` assignments and the test `1 ≤ pc` are secret-independent, and every source step is simulated by a bounded, secret-independent sequence of target steps whose leakage remains synchronized with the original. Under the paper’s leakage model, flattening therefore does not create new timing or control-flow side channels if the source program was already constant-time [2003.05836].

## 6. Scope, limitations, and common misconceptions

One common misconception is that the drastic CFG distortion of CFF necessarily changes program meaning. Both lines of work reject that view at different levels of abstraction. The deobfuscation study treats semantic preservation as the baseline property of obfuscation and measures how closely deobfuscated code returns to the original behavior [2604.15390]. The constant-time study proves, for a canonical scheme, that flattening preserves observational non-interference in addition to preserving functional behavior [2003.05836].

A second misconception is that successful structural recovery implies reliable semantic recovery. The deobfuscation results show that this is false in practice: code can exhibit a largely correct CFG while still omitting headers, misdeclaring data structures, or changing algorithmic behavior through semantic hallucination [2604.15390]. Structural and semantic metrics therefore serve different purposes.

A third misconception is that CFF in isolation remains uniformly resilient to modern automated analysis. The reported results suggest that, against CoT-guided LLMs, vanilla CFF is less effective than one might hope when it relies on fairly stereotyped dispatcher patterns [2604.15390]. However, the same results also show that stronger Opaque-CFF settings, especially with diverse opaque predicate patterns and complex original CFGs, remain challenging, and layered obfuscation still offers substantial resistance [2604.15390].

The scope of the constant-time theorem is also limited. It is proved for a small structured while-language without pointers, undefined behavior, concurrency, or microarchitectural effects such as caches and pipelines [2003.05836]. The attacker is passive, and the transformation studied is exactly the canonical flattening scheme defined through the dispatcher and `pc`; variants that use more complex opaque predicates or interleave unrelated code are not covered [2003.05836]. By contrast, the LLM deobfuscation study works on source, LLVM-IR, or binaries derived from Tigress and O-LLVM, but validates semantic preservation through execution and BLEU rather than by a formal equivalence proof [2604.15390].

## 7. Research context and practical significance

Within obfuscation research, CFF is characterized as a widely used structural transformation for C/C++ and LLVM-level code, and as one of the most widely used advanced obfuscations in industrial tools such as Obfuscator-LLVM [2604.15390][2003.05836]. It sits alongside other control-flow obfuscations such as opaque predicates, bogus control flow, control-flow locking, and virtualization [2604.15390]. Prior deobfuscation approaches include static analysis and symbolic execution, which suffer from path explosion under CFF and virtualization, dynamic analysis, which only covers observed paths and can be evaded, and machine-learning-based graph methods such as DFSGraph and neural graph embeddings for flattened binaries [2604.15390].

The CoT-guided LLM approach is positioned as complementary to, rather than a replacement for, static and dynamic tools [2604.15390]. It does not require dynamic execution and does not need training or fine-tuning on obfuscated corpora, instead using prompt engineering to guide general-purpose reasoning models through dispatcher recovery, state-transition extraction, opaque predicate analysis, and CFG restructuring [2604.15390]. The constant-time study, in turn, is presented as a first step toward a broader theory of security-preserving obfuscations, asking which obfuscation passes preserve which security properties and how CT-simulations can serve as a proof harness for such questions [2003.05836].

Taken together, these results define CFF as a transformation with a dual research identity. As an obfuscation, it replaces structured CFGs with dispatcher-driven state machines that significantly complicate static analysis. As a target of deobfuscation, it can often be substantially reversed by CoT-guided LLMs, although layered obfuscation and semantic hallucinations remain major obstacles. As a formally specified compiler pass, it can preserve a precise constant-time policy under a standard leakage semantics and a canonical dispatcher construction [2604.15390][2003.05836].

Source: https://www.emergentmind.com/topics/control-flow-flattening