@@ -737,19 +737,14 @@ template <typename StateTy> class CFGUnawareAnalysis {
737737//
738738// Then, a function can be split into a number of disjoint contiguous sequences
739739// 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).
742743class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis ,
743744 public CFGUnawareAnalysis<SrcState> {
744745 using SrcSafetyAnalysis::BC;
745746 BinaryFunction &BF;
746747
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-
753748public:
754749 CFGUnawareSrcSafetyAnalysis (BinaryFunction &BF,
755750 MCPlusBuilder::AllocatorIdTy AllocId,
@@ -759,6 +754,7 @@ class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis,
759754 }
760755
761756 void run () override {
757+ const SrcState DefaultState = computePessimisticState (BF);
762758 SrcState S = createEntryState ();
763759 for (auto &I : BF.instrs ()) {
764760 MCInst &Inst = I.second ;
@@ -773,7 +769,7 @@ class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis,
773769 LLVM_DEBUG ({
774770 traceInst (BC, " Due to label, resetting the state before" , Inst);
775771 });
776- S = createUnsafeState () ;
772+ S = DefaultState ;
777773 }
778774
779775 // Attach the state *before* this instruction executes.
@@ -1302,6 +1298,83 @@ shouldReportReturnGadget(const BinaryContext &BC, const MCInstReference &Inst,
13021298 return make_gadget_report (RetKind, Inst, *RetReg);
13031299}
13041300
1301+ // / While BOLT already marks some of the branch instructions as tail calls,
1302+ // / this function tries to improve the coverage by including less obvious cases
1303+ // / when it is possible to do without introducing too many false positives.
1304+ static bool shouldAnalyzeTailCallInst (const BinaryContext &BC,
1305+ const BinaryFunction &BF,
1306+ const MCInstReference &Inst) {
1307+ // Some BC.MIB->isXYZ(Inst) methods simply delegate to MCInstrDesc::isXYZ()
1308+ // (such as isBranch at the time of writing this comment), some don't (such
1309+ // as isCall). For that reason, call MCInstrDesc's methods explicitly when
1310+ // it is important.
1311+ const MCInstrDesc &Desc =
1312+ BC.MII ->get (static_cast <const MCInst &>(Inst).getOpcode ());
1313+ // Tail call should be a branch (but not necessarily an indirect one).
1314+ if (!Desc.isBranch ())
1315+ return false ;
1316+
1317+ // Always analyze the branches already marked as tail calls by BOLT.
1318+ if (BC.MIB ->isTailCall (Inst))
1319+ return true ;
1320+
1321+ // Try to also check the branches marked as "UNKNOWN CONTROL FLOW" - the
1322+ // below is a simplified condition from BinaryContext::printInstruction.
1323+ bool IsUnknownControlFlow =
1324+ BC.MIB ->isIndirectBranch (Inst) && !BC.MIB ->getJumpTable (Inst);
1325+
1326+ if (BF.hasCFG () && IsUnknownControlFlow)
1327+ return true ;
1328+
1329+ return false ;
1330+ }
1331+
1332+ static std::optional<PartialReport<MCPhysReg>>
1333+ shouldReportUnsafeTailCall (const BinaryContext &BC, const BinaryFunction &BF,
1334+ const MCInstReference &Inst, const SrcState &S) {
1335+ static const GadgetKind UntrustedLRKind (
1336+ " untrusted link register found before tail call" );
1337+
1338+ if (!shouldAnalyzeTailCallInst (BC, BF, Inst))
1339+ return std::nullopt ;
1340+
1341+ // Not only the set of registers returned by getTrustedLiveInRegs() can be
1342+ // seen as a reasonable target-independent _approximation_ of "the LR", these
1343+ // are *exactly* those registers used by SrcSafetyAnalysis to initialize the
1344+ // set of trusted registers on function entry.
1345+ // Thus, this function basically checks that the precondition expected to be
1346+ // imposed by a function call instruction (which is hardcoded into the target-
1347+ // specific getTrustedLiveInRegs() function) is also respected on tail calls.
1348+ SmallVector<MCPhysReg> RegsToCheck = BC.MIB ->getTrustedLiveInRegs ();
1349+ LLVM_DEBUG ({
1350+ traceInst (BC, " Found tail call inst" , Inst);
1351+ traceRegMask (BC, " Trusted regs" , S.TrustedRegs );
1352+ });
1353+
1354+ // In musl on AArch64, the _start function sets LR to zero and calls the next
1355+ // stage initialization function at the end, something along these lines:
1356+ //
1357+ // _start:
1358+ // mov x30, #0
1359+ // ; ... other initialization ...
1360+ // b _start_c ; performs "exit" system call at some point
1361+ //
1362+ // As this would produce a false positive for every executable linked with
1363+ // such libc, ignore tail calls performed by ELF entry function.
1364+ if (BC.StartFunctionAddress &&
1365+ *BC.StartFunctionAddress == Inst.getFunction ()->getAddress ()) {
1366+ LLVM_DEBUG ({ dbgs () << " Skipping tail call in ELF entry function.\n " ; });
1367+ return std::nullopt ;
1368+ }
1369+
1370+ // Returns at most one report per instruction - this is probably OK...
1371+ for (auto Reg : RegsToCheck)
1372+ if (!S.TrustedRegs [Reg])
1373+ return make_gadget_report (UntrustedLRKind, Inst, Reg);
1374+
1375+ return std::nullopt ;
1376+ }
1377+
13051378static std::optional<PartialReport<MCPhysReg>>
13061379shouldReportCallGadget (const BinaryContext &BC, const MCInstReference &Inst,
13071380 const SrcState &S) {
@@ -1457,6 +1530,9 @@ void FunctionAnalysisContext::findUnsafeUses(
14571530 if (PacRetGadgetsOnly)
14581531 return ;
14591532
1533+ if (auto Report = shouldReportUnsafeTailCall (BC, BF, Inst, S))
1534+ Reports.push_back (*Report);
1535+
14601536 if (auto Report = shouldReportCallGadget (BC, Inst, S))
14611537 Reports.push_back (*Report);
14621538 if (auto Report = shouldReportSigningOracle (BC, Inst, S))
0 commit comments