Skip to content

Commit 8b1fff4

Browse files
committed
big update 2
1 parent e858e45 commit 8b1fff4

17 files changed

Lines changed: 1730 additions & 270 deletions

src/shifting_codes/passes/bogus_control_flow.py

Lines changed: 190 additions & 67 deletions
Large diffs are not rendered by default.
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
"""[Pluto] Bogus Control Flow Pass — port of Pluto BogusControlFlowPass.cpp.
2+
3+
Splits each basic block into head/body/tail, clones the body block,
4+
and reconnects them with opaque predicates (always-true conditions)
5+
to confuse static analysis.
6+
"""
7+
8+
from __future__ import annotations
9+
10+
import llvm
11+
12+
from shifting_codes.passes import PassRegistry
13+
from shifting_codes.passes.base import FunctionPass, PassInfo
14+
from shifting_codes.utils.crypto import CryptoRandom
15+
16+
17+
def _clone_basic_block(body_bb: llvm.BasicBlock, func: llvm.Function,
18+
ctx: llvm.Context, tag: int) -> llvm.BasicBlock:
19+
"""Clone a basic block's instructions into a new block.
20+
21+
Creates a new block with cloned copies of all instructions from body_bb.
22+
Operand references to values defined within body_bb are remapped to their
23+
cloned counterparts.
24+
"""
25+
clone_bb = func.append_basic_block(f"bcf.clone.{tag}")
26+
27+
# Map from original values to cloned values
28+
value_map: dict = {}
29+
30+
with clone_bb.create_builder() as builder:
31+
for inst in body_bb.instructions:
32+
cloned = inst.instruction_clone()
33+
builder.insert_into_builder_with_name(cloned, "")
34+
value_map[inst] = cloned
35+
36+
# Remap operands: replace references to original values with cloned ones
37+
for inst in clone_bb.instructions:
38+
for i in range(inst.num_operands):
39+
op = inst.get_operand(i)
40+
if op in value_map:
41+
inst.set_operand(i, value_map[op])
42+
43+
return clone_bb
44+
45+
46+
def _create_bogus_cmp(builder, mod: llvm.Module, i32, tag: int, suffix: str):
47+
"""Create opaque predicate: (y < 10 || x*(x+1) % 2 == 0) — always true."""
48+
x_gv = mod.add_global(i32, f"__bcf_x_{tag}_{suffix}")
49+
x_gv.initializer = i32.constant(0)
50+
x_gv.linkage = llvm.Linkage.Private
51+
52+
y_gv = mod.add_global(i32, f"__bcf_y_{tag}_{suffix}")
53+
y_gv.initializer = i32.constant(0)
54+
y_gv.linkage = llvm.Linkage.Private
55+
56+
x_val = builder.load(i32, x_gv, "bcf.x")
57+
y_val = builder.load(i32, y_gv, "bcf.y")
58+
59+
# cond1: y < 10
60+
cond1 = builder.icmp(llvm.IntPredicate.SLT, y_val, i32.constant(10), "bcf.cmp1")
61+
62+
# cond2: x * (x + 1) % 2 == 0 (always true)
63+
xp1 = builder.add(x_val, i32.constant(1), "bcf.xp1")
64+
xmul = builder.mul(x_val, xp1, "bcf.xmul")
65+
xmod = builder.urem(xmul, i32.constant(2), "bcf.xmod")
66+
cond2 = builder.icmp(llvm.IntPredicate.EQ, xmod, i32.constant(0), "bcf.cmp2")
67+
68+
return builder.or_(cond1, cond2, "bcf.cond")
69+
70+
71+
@PassRegistry.register
72+
class PlutoBogusControlFlowPass(FunctionPass):
73+
74+
def __init__(self, rng: CryptoRandom | None = None):
75+
self.rng = rng or CryptoRandom()
76+
self._bcf_counter = 0
77+
78+
@classmethod
79+
def info(cls) -> PassInfo:
80+
return PassInfo(
81+
name="bogus_control_flow_pluto",
82+
description="[Pluto] Insert opaque predicates and bogus branches",
83+
)
84+
85+
def run_on_function(self, func: llvm.Function, ctx: llvm.Context) -> bool:
86+
mod = func.module
87+
i32 = ctx.types.i32
88+
changed = False
89+
90+
# Collect original blocks (snapshot — we'll be modifying the CFG)
91+
orig_blocks = list(func.basic_blocks)
92+
93+
for bb in orig_blocks:
94+
term = bb.terminator
95+
if term is None:
96+
continue
97+
# Skip invoke and exception-handling blocks
98+
if term.opcode == llvm.Opcode.Invoke:
99+
continue
100+
101+
self._bcf_counter += 1
102+
tag = self._bcf_counter
103+
104+
# Step 1: Split into headBB | bodyBB | tailBB
105+
# Find the first non-PHI instruction to split at
106+
first_non_phi = None
107+
for inst in bb.instructions:
108+
if inst.opcode != llvm.Opcode.PHI:
109+
first_non_phi = inst
110+
break
111+
112+
if first_non_phi is None:
113+
continue
114+
115+
# If block has only a terminator, skip (nothing to split)
116+
if first_non_phi.is_terminator:
117+
continue
118+
119+
# Split: headBB → bodyBB (at first non-PHI)
120+
body_bb = bb.split_basic_block(first_non_phi, f"bcf.body.{tag}")
121+
122+
# Split: bodyBB → tailBB (at bodyBB's terminator)
123+
body_term = body_bb.terminator
124+
if body_term is None:
125+
continue
126+
tail_bb = body_bb.split_basic_block(body_term, f"bcf.tail.{tag}")
127+
128+
# Step 2: Clone bodyBB
129+
clone_bb = _clone_basic_block(body_bb, func, ctx, tag)
130+
131+
# Step 3: Rewire with bogus branches
132+
# 3.1: Remove unconditional branches from headBB, bodyBB, cloneBB
133+
head_term = bb.terminator
134+
body_term = body_bb.terminator
135+
clone_term = clone_bb.terminator
136+
137+
head_term.erase_from_parent()
138+
body_term.erase_from_parent()
139+
clone_term.erase_from_parent()
140+
141+
# 3.2: headBB → (cond1 ? bodyBB : cloneBB)
142+
with bb.create_builder() as builder:
143+
cond1 = _create_bogus_cmp(builder, mod, i32, tag, "head")
144+
builder.cond_br(cond1, body_bb, clone_bb)
145+
146+
# 3.3: bodyBB → (cond2 ? tailBB : cloneBB)
147+
with body_bb.create_builder() as builder:
148+
cond2 = _create_bogus_cmp(builder, mod, i32, tag, "body")
149+
builder.cond_br(cond2, tail_bb, clone_bb)
150+
151+
# 3.4: cloneBB → bodyBB (unconditional)
152+
with clone_bb.create_builder() as builder:
153+
builder.br(body_bb)
154+
155+
# Fix PHI nodes: bodyBB now has cloneBB as an additional predecessor
156+
for inst in body_bb.instructions:
157+
if inst.opcode == llvm.Opcode.PHI:
158+
inst.add_incoming(inst.type.undef(), clone_bb)
159+
else:
160+
break
161+
162+
changed = True
163+
164+
return changed

0 commit comments

Comments
 (0)