Skip to content

Commit 93e1a90

Browse files
committed
Fix handling of unreachable loops of BBs
1 parent 96e1b04 commit 93e1a90

File tree

3 files changed

+68
-14
lines changed

3 files changed

+68
-14
lines changed

bolt/lib/Passes/PAuthGadgetScanner.cpp

+38-11
Original file line numberDiff line numberDiff line change
@@ -1342,21 +1342,42 @@ void FunctionAnalysisContext::findUnsafeUses(
13421342
BF.dump();
13431343
});
13441344

1345+
bool UnreachableBBReported = false;
13451346
if (BF.hasCFG()) {
1346-
// Warn on basic blocks being unreachable according to BOLT, as this
1347-
// likely means CFG is imprecise.
1347+
// Warn on basic blocks being unreachable according to BOLT (at most once
1348+
// per BinaryFunction), as this likely means the CFG reconstructed by BOLT
1349+
// is imprecise. A basic block can be
1350+
// * reachable from an entry basic block - a hopefully correct non-empty
1351+
// state is propagated to that basic block sooner or later. All basic
1352+
// blocks are expected to belong to this category under normal conditions.
1353+
// * reachable from a "directly unreachable" BB (a basic block that has no
1354+
// direct predecessors and this is not because it is an entry BB) - *some*
1355+
// non-empty state is propagated to this basic block sooner or later, as
1356+
// the initial state of directly unreachable basic blocks is
1357+
// pessimistically initialized to "all registers are unsafe"
1358+
// - a warning can be printed for the "directly unreachable" basic block
1359+
// * neither reachable from an entry nor from a "directly unreachable" BB
1360+
// (such as if this BB is in an isolated loop of basic blocks) - the final
1361+
// state is computed to be empty for this basic block
1362+
// - a warning can be printed for this basic block
13481363
for (BinaryBasicBlock &BB : BF) {
1349-
if (!BB.pred_empty() || BB.isEntryPoint())
1364+
MCInst *FirstInst = BB.getFirstNonPseudoInstr();
1365+
// Skip empty basic block early for simplicity.
1366+
if (!FirstInst)
1367+
continue;
1368+
1369+
bool IsDirectlyUnreachable = BB.pred_empty() && !BB.isEntryPoint();
1370+
bool HasNoStateComputed = Analysis->getStateBefore(*FirstInst).empty();
1371+
if (!IsDirectlyUnreachable && !HasNoStateComputed)
13501372
continue;
1351-
// Arbitrarily attach the report to the first instruction of BB.
1352-
MCInst *InstToReport = BB.getFirstNonPseudoInstr();
1353-
if (!InstToReport)
1354-
continue; // BB has no real instructions
13551373

1374+
// Arbitrarily attach the report to the first instruction of BB.
13561375
Reports.push_back(
1357-
make_generic_report(MCInstReference::get(InstToReport, BF),
1358-
"Warning: no predecessor basic blocks detected "
1359-
"(possibly incomplete CFG)"));
1376+
make_generic_report(MCInstReference::get(FirstInst, BF),
1377+
"Warning: the function has unreachable basic "
1378+
"blocks (possibly incomplete CFG)"));
1379+
UnreachableBBReported = true;
1380+
break; // One warning per function.
13601381
}
13611382
}
13621383

@@ -1365,7 +1386,13 @@ void FunctionAnalysisContext::findUnsafeUses(
13651386
return;
13661387

13671388
const SrcState &S = Analysis->getStateBefore(Inst);
1368-
assert(!S.empty() && "Instruction has no associated state");
1389+
if (S.empty()) {
1390+
LLVM_DEBUG(
1391+
{ traceInst(BC, "Instruction has no state, skipping", Inst); });
1392+
assert(UnreachableBBReported && "Should be reported at least once");
1393+
(void)UnreachableBBReported;
1394+
return;
1395+
}
13691396

13701397
if (auto Report = shouldReportReturnGadget(BC, Inst, S))
13711398
Reports.push_back(*Report);

bolt/test/binary-analysis/AArch64/gs-pacret-autiasp.s

+1-1
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ f_callclobbered_calleesaved:
215215
.globl f_unreachable_instruction
216216
.type f_unreachable_instruction,@function
217217
f_unreachable_instruction:
218-
// CHECK-LABEL: GS-PAUTH: Warning: no predecessor basic blocks detected (possibly incomplete CFG) in function f_unreachable_instruction, basic block {{[0-9a-zA-Z.]+}}, at address
218+
// CHECK-LABEL: GS-PAUTH: Warning: the function has unreachable basic blocks (possibly incomplete CFG) in function f_unreachable_instruction, basic block {{[0-9a-zA-Z.]+}}, at address
219219
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: add x0, x1, x2
220220
// CHECK-NOT: instructions that write to the affected registers after any authentication are:
221221
// CHECK-LABEL: GS-PAUTH: non-protected ret found in function f_unreachable_instruction, basic block {{[0-9a-zA-Z.]+}}, at address

bolt/test/binary-analysis/AArch64/gs-pauth-calls.s

+29-2
Original file line numberDiff line numberDiff line change
@@ -1441,7 +1441,7 @@ printed_instrs_nocfg:
14411441
.globl bad_unreachable_call
14421442
.type bad_unreachable_call,@function
14431443
bad_unreachable_call:
1444-
// CHECK-LABEL: GS-PAUTH: Warning: no predecessor basic blocks detected (possibly incomplete CFG) in function bad_unreachable_call, basic block {{[^,]+}}, at address
1444+
// CHECK-LABEL: GS-PAUTH: Warning: the function has unreachable basic blocks (possibly incomplete CFG) in function bad_unreachable_call, basic block {{[^,]+}}, at address
14451445
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x0
14461446
// CHECK-NOT: instructions that write to the affected registers after any authentication are:
14471447
// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_unreachable_call, basic block {{[^,]+}}, at address
@@ -1465,7 +1465,7 @@ bad_unreachable_call:
14651465
.type good_unreachable_call,@function
14661466
good_unreachable_call:
14671467
// CHECK-NOT: non-protected call{{.*}}good_unreachable_call
1468-
// CHECK-LABEL: GS-PAUTH: Warning: no predecessor basic blocks detected (possibly incomplete CFG) in function good_unreachable_call, basic block {{[^,]+}}, at address
1468+
// CHECK-LABEL: GS-PAUTH: Warning: the function has unreachable basic blocks (possibly incomplete CFG) in function good_unreachable_call, basic block {{[^,]+}}, at address
14691469
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
14701470
// CHECK-NOT: instructions that write to the affected registers after any authentication are:
14711471
// CHECK-NOT: non-protected call{{.*}}good_unreachable_call
@@ -1485,6 +1485,33 @@ good_unreachable_call:
14851485
ret
14861486
.size good_unreachable_call, .-good_unreachable_call
14871487

1488+
.globl unreachable_loop_of_bbs
1489+
.type unreachable_loop_of_bbs,@function
1490+
unreachable_loop_of_bbs:
1491+
// CHECK-NOT: unreachable basic blocks{{.*}}unreachable_loop_of_bbs
1492+
// CHECK-NOT: non-protected call{{.*}}unreachable_loop_of_bbs
1493+
// CHECK-LABEL: GS-PAUTH: Warning: the function has unreachable basic blocks (possibly incomplete CFG) in function unreachable_loop_of_bbs, basic block {{[^,]+}}, at address
1494+
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x0
1495+
// CHECK-NOT: unreachable basic blocks{{.*}}unreachable_loop_of_bbs
1496+
// CHECK-NOT: non-protected call{{.*}}unreachable_loop_of_bbs
1497+
paciasp
1498+
stp x29, x30, [sp, #-16]!
1499+
mov x29, sp
1500+
b .Lreachable_epilogue_bb
1501+
1502+
.Lfirst_unreachable_bb:
1503+
blr x0 // <-- this call is not analyzed
1504+
b .Lsecond_unreachable_bb
1505+
.Lsecond_unreachable_bb:
1506+
blr x1 // <-- this call is not analyzed
1507+
b .Lfirst_unreachable_bb
1508+
1509+
.Lreachable_epilogue_bb:
1510+
ldp x29, x30, [sp], #16
1511+
autiasp
1512+
ret
1513+
.size unreachable_loop_of_bbs, .-unreachable_loop_of_bbs
1514+
14881515
.globl main
14891516
.type main,@function
14901517
main:

0 commit comments

Comments
 (0)