@@ -737,19 +737,14 @@ template <typename StateTy> class CFGUnawareAnalysis {
737
737
//
738
738
// Then, a function can be split into a number of disjoint contiguous sequences
739
739
// of instructions without labels in between. These sequences can be processed
740
- // the same way basic blocks are processed by data-flow analysis, assuming
741
- // pessimistically that all registers are unsafe at the start of each sequence.
740
+ // the same way basic blocks are processed by data-flow analysis, with the same
741
+ // pessimistic estimation of the initial state at the start of each sequence
742
+ // (except the first instruction of the function).
742
743
class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis ,
743
744
public CFGUnawareAnalysis<SrcState> {
744
745
using SrcSafetyAnalysis::BC;
745
746
BinaryFunction &BF;
746
747
747
- // / Creates a state with all registers marked unsafe (not to be confused
748
- // / with empty state).
749
- SrcState createUnsafeState () const {
750
- return SrcState (NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters ());
751
- }
752
-
753
748
public:
754
749
CFGUnawareSrcSafetyAnalysis (BinaryFunction &BF,
755
750
MCPlusBuilder::AllocatorIdTy AllocId,
@@ -759,6 +754,7 @@ class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis,
759
754
}
760
755
761
756
void run () override {
757
+ const SrcState DefaultState = computePessimisticState (BF);
762
758
SrcState S = createEntryState ();
763
759
for (auto &I : BF.instrs ()) {
764
760
MCInst &Inst = I.second ;
@@ -773,7 +769,7 @@ class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis,
773
769
LLVM_DEBUG ({
774
770
traceInst (BC, " Due to label, resetting the state before" , Inst);
775
771
});
776
- S = createUnsafeState () ;
772
+ S = DefaultState ;
777
773
}
778
774
779
775
// Attach the state *before* this instruction executes.
@@ -1311,6 +1307,83 @@ shouldReportReturnGadget(const BinaryContext &BC, const MCInstReference &Inst,
1311
1307
return make_gadget_report (RetKind, Inst, *RetReg);
1312
1308
}
1313
1309
1310
+ // / While BOLT already marks some of the branch instructions as tail calls,
1311
+ // / this function tries to improve the coverage by including less obvious cases
1312
+ // / when it is possible to do without introducing too many false positives.
1313
+ static bool shouldAnalyzeTailCallInst (const BinaryContext &BC,
1314
+ const BinaryFunction &BF,
1315
+ const MCInstReference &Inst) {
1316
+ // Some BC.MIB->isXYZ(Inst) methods simply delegate to MCInstrDesc::isXYZ()
1317
+ // (such as isBranch at the time of writing this comment), some don't (such
1318
+ // as isCall). For that reason, call MCInstrDesc's methods explicitly when
1319
+ // it is important.
1320
+ const MCInstrDesc &Desc =
1321
+ BC.MII ->get (static_cast <const MCInst &>(Inst).getOpcode ());
1322
+ // Tail call should be a branch (but not necessarily an indirect one).
1323
+ if (!Desc.isBranch ())
1324
+ return false ;
1325
+
1326
+ // Always analyze the branches already marked as tail calls by BOLT.
1327
+ if (BC.MIB ->isTailCall (Inst))
1328
+ return true ;
1329
+
1330
+ // Try to also check the branches marked as "UNKNOWN CONTROL FLOW" - the
1331
+ // below is a simplified condition from BinaryContext::printInstruction.
1332
+ bool IsUnknownControlFlow =
1333
+ BC.MIB ->isIndirectBranch (Inst) && !BC.MIB ->getJumpTable (Inst);
1334
+
1335
+ if (BF.hasCFG () && IsUnknownControlFlow)
1336
+ return true ;
1337
+
1338
+ return false ;
1339
+ }
1340
+
1341
+ static std::optional<PartialReport<MCPhysReg>>
1342
+ shouldReportUnsafeTailCall (const BinaryContext &BC, const BinaryFunction &BF,
1343
+ const MCInstReference &Inst, const SrcState &S) {
1344
+ static const GadgetKind UntrustedLRKind (
1345
+ " untrusted link register found before tail call" );
1346
+
1347
+ if (!shouldAnalyzeTailCallInst (BC, BF, Inst))
1348
+ return std::nullopt;
1349
+
1350
+ // Not only the set of registers returned by getTrustedLiveInRegs() can be
1351
+ // seen as a reasonable target-independent _approximation_ of "the LR", these
1352
+ // are *exactly* those registers used by SrcSafetyAnalysis to initialize the
1353
+ // set of trusted registers on function entry.
1354
+ // Thus, this function basically checks that the precondition expected to be
1355
+ // imposed by a function call instruction (which is hardcoded into the target-
1356
+ // specific getTrustedLiveInRegs() function) is also respected on tail calls.
1357
+ SmallVector<MCPhysReg> RegsToCheck = BC.MIB ->getTrustedLiveInRegs ();
1358
+ LLVM_DEBUG ({
1359
+ traceInst (BC, " Found tail call inst" , Inst);
1360
+ traceRegMask (BC, " Trusted regs" , S.TrustedRegs );
1361
+ });
1362
+
1363
+ // In musl on AArch64, the _start function sets LR to zero and calls the next
1364
+ // stage initialization function at the end, something along these lines:
1365
+ //
1366
+ // _start:
1367
+ // mov x30, #0
1368
+ // ; ... other initialization ...
1369
+ // b _start_c ; performs "exit" system call at some point
1370
+ //
1371
+ // As this would produce a false positive for every executable linked with
1372
+ // such libc, ignore tail calls performed by ELF entry function.
1373
+ if (BC.StartFunctionAddress &&
1374
+ *BC.StartFunctionAddress == Inst.getFunction ()->getAddress ()) {
1375
+ LLVM_DEBUG ({ dbgs () << " Skipping tail call in ELF entry function.\n " ; });
1376
+ return std::nullopt;
1377
+ }
1378
+
1379
+ // Returns at most one report per instruction - this is probably OK...
1380
+ for (auto Reg : RegsToCheck)
1381
+ if (!S.TrustedRegs [Reg])
1382
+ return make_gadget_report (UntrustedLRKind, Inst, Reg);
1383
+
1384
+ return std::nullopt;
1385
+ }
1386
+
1314
1387
static std::optional<PartialReport<MCPhysReg>>
1315
1388
shouldReportCallGadget (const BinaryContext &BC, const MCInstReference &Inst,
1316
1389
const SrcState &S) {
@@ -1466,6 +1539,9 @@ void FunctionAnalysisContext::findUnsafeUses(
1466
1539
if (PacRetGadgetsOnly)
1467
1540
return ;
1468
1541
1542
+ if (auto Report = shouldReportUnsafeTailCall (BC, BF, Inst, S))
1543
+ Reports.push_back (*Report);
1544
+
1469
1545
if (auto Report = shouldReportCallGadget (BC, Inst, S))
1470
1546
Reports.push_back (*Report);
1471
1547
if (auto Report = shouldReportSigningOracle (BC, Inst, S))
0 commit comments