@@ -701,8 +701,9 @@ class DataflowSrcSafetyAnalysis
701
701
//
702
702
// Then, a function can be split into a number of disjoint contiguous sequences
703
703
// of instructions without labels in between. These sequences can be processed
704
- // the same way basic blocks are processed by data-flow analysis, assuming
705
- // pessimistically that all registers are unsafe at the start of each sequence.
704
+ // the same way basic blocks are processed by data-flow analysis, with the same
705
+ // pessimistic estimation of the initial state at the start of each sequence
706
+ // (except the first instruction of the function).
706
707
class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis {
707
708
BinaryFunction &BF;
708
709
MCPlusBuilder::AllocatorIdTy AllocId;
@@ -713,12 +714,6 @@ class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis {
713
714
BC.MIB ->removeAnnotation (I.second , StateAnnotationIndex);
714
715
}
715
716
716
- // / Creates a state with all registers marked unsafe (not to be confused
717
- // / with empty state).
718
- SrcState createUnsafeState () const {
719
- return SrcState (NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters ());
720
- }
721
-
722
717
public:
723
718
CFGUnawareSrcSafetyAnalysis (BinaryFunction &BF,
724
719
MCPlusBuilder::AllocatorIdTy AllocId,
@@ -729,6 +724,7 @@ class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis {
729
724
}
730
725
731
726
void run () override {
727
+ const SrcState DefaultState = computePessimisticState (BF);
732
728
SrcState S = createEntryState ();
733
729
for (auto &I : BF.instrs ()) {
734
730
MCInst &Inst = I.second ;
@@ -743,7 +739,7 @@ class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis {
743
739
LLVM_DEBUG ({
744
740
traceInst (BC, " Due to label, resetting the state before" , Inst);
745
741
});
746
- S = createUnsafeState () ;
742
+ S = DefaultState ;
747
743
}
748
744
749
745
// Check if we need to remove an old annotation (this is the case if
@@ -1288,6 +1284,83 @@ shouldReportReturnGadget(const BinaryContext &BC, const MCInstReference &Inst,
1288
1284
return make_gadget_report (RetKind, Inst, *RetReg);
1289
1285
}
1290
1286
1287
+ // / While BOLT already marks some of the branch instructions as tail calls,
1288
+ // / this function tries to improve the coverage by including less obvious cases
1289
+ // / when it is possible to do without introducing too many false positives.
1290
+ static bool shouldAnalyzeTailCallInst (const BinaryContext &BC,
1291
+ const BinaryFunction &BF,
1292
+ const MCInstReference &Inst) {
1293
+ // Some BC.MIB->isXYZ(Inst) methods simply delegate to MCInstrDesc::isXYZ()
1294
+ // (such as isBranch at the time of writing this comment), some don't (such
1295
+ // as isCall). For that reason, call MCInstrDesc's methods explicitly when
1296
+ // it is important.
1297
+ const MCInstrDesc &Desc =
1298
+ BC.MII ->get (static_cast <const MCInst &>(Inst).getOpcode ());
1299
+ // Tail call should be a branch (but not necessarily an indirect one).
1300
+ if (!Desc.isBranch ())
1301
+ return false ;
1302
+
1303
+ // Always analyze the branches already marked as tail calls by BOLT.
1304
+ if (BC.MIB ->isTailCall (Inst))
1305
+ return true ;
1306
+
1307
+ // Try to also check the branches marked as "UNKNOWN CONTROL FLOW" - the
1308
+ // below is a simplified condition from BinaryContext::printInstruction.
1309
+ bool IsUnknownControlFlow =
1310
+ BC.MIB ->isIndirectBranch (Inst) && !BC.MIB ->getJumpTable (Inst);
1311
+
1312
+ if (BF.hasCFG () && IsUnknownControlFlow)
1313
+ return true ;
1314
+
1315
+ return false ;
1316
+ }
1317
+
1318
+ static std::optional<PartialReport<MCPhysReg>>
1319
+ shouldReportUnsafeTailCall (const BinaryContext &BC, const BinaryFunction &BF,
1320
+ const MCInstReference &Inst, const SrcState &S) {
1321
+ static const GadgetKind UntrustedLRKind (
1322
+ " untrusted link register found before tail call" );
1323
+
1324
+ if (!shouldAnalyzeTailCallInst (BC, BF, Inst))
1325
+ return std::nullopt;
1326
+
1327
+ // Not only the set of registers returned by getTrustedLiveInRegs() can be
1328
+ // seen as a reasonable target-independent _approximation_ of "the LR", these
1329
+ // are *exactly* those registers used by SrcSafetyAnalysis to initialize the
1330
+ // set of trusted registers on function entry.
1331
+ // Thus, this function basically checks that the precondition expected to be
1332
+ // imposed by a function call instruction (which is hardcoded into the target-
1333
+ // specific getTrustedLiveInRegs() function) is also respected on tail calls.
1334
+ SmallVector<MCPhysReg> RegsToCheck = BC.MIB ->getTrustedLiveInRegs ();
1335
+ LLVM_DEBUG ({
1336
+ traceInst (BC, " Found tail call inst" , Inst);
1337
+ traceRegMask (BC, " Trusted regs" , S.TrustedRegs );
1338
+ });
1339
+
1340
+ // In musl on AArch64, the _start function sets LR to zero and calls the next
1341
+ // stage initialization function at the end, something along these lines:
1342
+ //
1343
+ // _start:
1344
+ // mov x30, #0
1345
+ // ; ... other initialization ...
1346
+ // b _start_c ; performs "exit" system call at some point
1347
+ //
1348
+ // As this would produce a false positive for every executable linked with
1349
+ // such libc, ignore tail calls performed by ELF entry function.
1350
+ if (BC.StartFunctionAddress &&
1351
+ *BC.StartFunctionAddress == Inst.getFunction ()->getAddress ()) {
1352
+ LLVM_DEBUG ({ dbgs () << " Skipping tail call in ELF entry function.\n " ; });
1353
+ return std::nullopt;
1354
+ }
1355
+
1356
+ // Returns at most one report per instruction - this is probably OK...
1357
+ for (auto Reg : RegsToCheck)
1358
+ if (!S.TrustedRegs [Reg])
1359
+ return make_gadget_report (UntrustedLRKind, Inst, Reg);
1360
+
1361
+ return std::nullopt;
1362
+ }
1363
+
1291
1364
static std::optional<PartialReport<MCPhysReg>>
1292
1365
shouldReportCallGadget (const BinaryContext &BC, const MCInstReference &Inst,
1293
1366
const SrcState &S) {
@@ -1444,6 +1517,9 @@ void FunctionAnalysisContext::findUnsafeUses(
1444
1517
if (PacRetGadgetsOnly)
1445
1518
return ;
1446
1519
1520
+ if (auto Report = shouldReportUnsafeTailCall (BC, BF, Inst, S))
1521
+ Reports.push_back (*Report);
1522
+
1447
1523
if (auto Report = shouldReportCallGadget (BC, Inst, S))
1448
1524
Reports.push_back (*Report);
1449
1525
if (auto Report = shouldReportSigningOracle (BC, Inst, S))
0 commit comments