@@ -701,8 +701,9 @@ class DataflowSrcSafetyAnalysis
701701//
702702// Then, a function can be split into a number of disjoint contiguous sequences
703703// 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).
706707class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis {
707708 BinaryFunction &BF;
708709 MCPlusBuilder::AllocatorIdTy AllocId;
@@ -713,12 +714,6 @@ class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis {
713714 BC.MIB ->removeAnnotation (I.second , StateAnnotationIndex);
714715 }
715716
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-
722717public:
723718 CFGUnawareSrcSafetyAnalysis (BinaryFunction &BF,
724719 MCPlusBuilder::AllocatorIdTy AllocId,
@@ -729,6 +724,7 @@ class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis {
729724 }
730725
731726 void run () override {
727+ const SrcState DefaultState = computePessimisticState (BF);
732728 SrcState S = createEntryState ();
733729 for (auto &I : BF.instrs ()) {
734730 MCInst &Inst = I.second ;
@@ -743,7 +739,7 @@ class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis {
743739 LLVM_DEBUG ({
744740 traceInst (BC, " Due to label, resetting the state before" , Inst);
745741 });
746- S = createUnsafeState () ;
742+ S = DefaultState ;
747743 }
748744
749745 // 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,
12881284 return make_gadget_report (RetKind, Inst, *RetReg);
12891285}
12901286
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+
12911364static std::optional<PartialReport<MCPhysReg>>
12921365shouldReportCallGadget (const BinaryContext &BC, const MCInstReference &Inst,
12931366 const SrcState &S) {
@@ -1444,6 +1517,9 @@ void FunctionAnalysisContext::findUnsafeUses(
14441517 if (PacRetGadgetsOnly)
14451518 return ;
14461519
1520+ if (auto Report = shouldReportUnsafeTailCall (BC, BF, Inst, S))
1521+ Reports.push_back (*Report);
1522+
14471523 if (auto Report = shouldReportCallGadget (BC, Inst, S))
14481524 Reports.push_back (*Report);
14491525 if (auto Report = shouldReportSigningOracle (BC, Inst, S))
0 commit comments