Skip to content

Commit 49f2679

Browse files
committed
Fix handling of unreachable loops of BBs
1 parent c11b901 commit 49f2679

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
@@ -1349,21 +1349,42 @@ void FunctionAnalysisContext::findUnsafeUses(
13491349
BF.dump();
13501350
});
13511351

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

1381+
// Arbitrarily attach the report to the first instruction of BB.
13631382
Reports.push_back(
1364-
make_generic_report(MCInstReference::get(InstToReport, BF),
1365-
"Warning: no predecessor basic blocks detected "
1366-
"(possibly incomplete CFG)"));
1383+
make_generic_report(MCInstReference::get(FirstInst, BF),
1384+
"Warning: the function has unreachable basic "
1385+
"blocks (possibly incomplete CFG)"));
1386+
UnreachableBBReported = true;
1387+
break; // One warning per function.
13671388
}
13681389
}
13691390

@@ -1372,7 +1393,13 @@ void FunctionAnalysisContext::findUnsafeUses(
13721393
return;
13731394

13741395
const SrcState &S = Analysis->getStateBefore(Inst);
1375-
assert(!S.empty() && "Instruction has no associated state");
1396+
if (S.empty()) {
1397+
LLVM_DEBUG(
1398+
{ traceInst(BC, "Instruction has no state, skipping", Inst); });
1399+
assert(UnreachableBBReported && "Should be reported at least once");
1400+
(void)UnreachableBBReported;
1401+
return;
1402+
}
13761403

13771404
if (auto Report = shouldReportReturnGadget(BC, Inst, S))
13781405
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)