Skip to content

Commit a5ac9f3

Browse files
committed
Fix handling of unreachable loops of BBs
1 parent c63cd75 commit a5ac9f3

File tree

3 files changed

+68
-14
lines changed

3 files changed

+68
-14
lines changed

bolt/lib/Passes/PAuthGadgetScanner.cpp

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,21 +1371,42 @@ void FunctionAnalysisContext::findUnsafeUses(
13711371
BF.dump();
13721372
});
13731373

1374+
bool UnreachableBBReported = false;
13741375
if (BF.hasCFG()) {
1375-
// Warn on basic blocks being unreachable according to BOLT, as this
1376-
// likely means CFG is imprecise.
1376+
// Warn on basic blocks being unreachable according to BOLT (at most once
1377+
// per BinaryFunction), as this likely means the CFG reconstructed by BOLT
1378+
// is imprecise. A basic block can be
1379+
// * reachable from an entry basic block - a hopefully correct non-empty
1380+
// state is propagated to that basic block sooner or later. All basic
1381+
// blocks are expected to belong to this category under normal conditions.
1382+
// * reachable from a "directly unreachable" BB (a basic block that has no
1383+
// direct predecessors and this is not because it is an entry BB) - *some*
1384+
// non-empty state is propagated to this basic block sooner or later, as
1385+
// the initial state of directly unreachable basic blocks is
1386+
// pessimistically initialized to "all registers are unsafe"
1387+
// - a warning can be printed for the "directly unreachable" basic block
1388+
// * neither reachable from an entry nor from a "directly unreachable" BB
1389+
// (such as if this BB is in an isolated loop of basic blocks) - the final
1390+
// state is computed to be empty for this basic block
1391+
// - a warning can be printed for this basic block
13771392
for (BinaryBasicBlock &BB : BF) {
1378-
if (!BB.pred_empty() || BB.isEntryPoint())
1393+
MCInst *FirstInst = BB.getFirstNonPseudoInstr();
1394+
// Skip empty basic block early for simplicity.
1395+
if (!FirstInst)
1396+
continue;
1397+
1398+
bool IsDirectlyUnreachable = BB.pred_empty() && !BB.isEntryPoint();
1399+
bool HasNoStateComputed = Analysis->getStateBefore(*FirstInst).empty();
1400+
if (!IsDirectlyUnreachable && !HasNoStateComputed)
13791401
continue;
1380-
// Arbitrarily attach the report to the first instruction of BB.
1381-
MCInst *InstToReport = BB.getFirstNonPseudoInstr();
1382-
if (!InstToReport)
1383-
continue; // BB has no real instructions
13841402

1403+
// Arbitrarily attach the report to the first instruction of BB.
13851404
Reports.push_back(
1386-
make_generic_report(MCInstReference::get(InstToReport, BF),
1387-
"Warning: no predecessor basic blocks detected "
1388-
"(possibly incomplete CFG)"));
1405+
make_generic_report(MCInstReference::get(FirstInst, BF),
1406+
"Warning: the function has unreachable basic "
1407+
"blocks (possibly incomplete CFG)"));
1408+
UnreachableBBReported = true;
1409+
break; // One warning per function.
13891410
}
13901411
}
13911412

@@ -1394,7 +1415,13 @@ void FunctionAnalysisContext::findUnsafeUses(
13941415
return;
13951416

13961417
const SrcState &S = Analysis->getStateBefore(Inst);
1397-
assert(!S.empty() && "Instruction has no associated state");
1418+
if (S.empty()) {
1419+
LLVM_DEBUG(
1420+
{ traceInst(BC, "Instruction has no state, skipping", Inst); });
1421+
assert(UnreachableBBReported && "Should be reported at least once");
1422+
(void)UnreachableBBReported;
1423+
return;
1424+
}
13981425

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

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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 29 additions & 2 deletions
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)