@@ -736,19 +736,14 @@ template <typename StateTy> class CFGUnawareAnalysis {
736736//
737737// Then, a function can be split into a number of disjoint contiguous sequences
738738// of instructions without labels in between. These sequences can be processed
739- // the same way basic blocks are processed by data-flow analysis, assuming
740- // pessimistically that all registers are unsafe at the start of each sequence.
739+ // the same way basic blocks are processed by data-flow analysis, with the same
740+ // pessimistic estimation of the initial state at the start of each sequence
741+ // (except the first instruction of the function).
741742class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis ,
742743 public CFGUnawareAnalysis<SrcState> {
743744 using SrcSafetyAnalysis::BC;
744745 BinaryFunction &BF;
745746
746- // / Creates a state with all registers marked unsafe (not to be confused
747- // / with empty state).
748- SrcState createUnsafeState () const {
749- return SrcState (NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters ());
750- }
751-
752747public:
753748 CFGUnawareSrcSafetyAnalysis (BinaryFunction &BF,
754749 MCPlusBuilder::AllocatorIdTy AllocId,
@@ -758,6 +753,7 @@ class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis,
758753 }
759754
760755 void run () override {
756+ const SrcState DefaultState = computePessimisticState (BF);
761757 SrcState S = createEntryState ();
762758 for (auto &I : BF.instrs ()) {
763759 MCInst &Inst = I.second ;
@@ -772,7 +768,7 @@ class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis,
772768 LLVM_DEBUG ({
773769 traceInst (BC, " Due to label, resetting the state before" , Inst);
774770 });
775- S = createUnsafeState () ;
771+ S = DefaultState ;
776772 }
777773
778774 // Attach the state *before* this instruction executes.
@@ -1297,6 +1293,83 @@ shouldReportReturnGadget(const BinaryContext &BC, const MCInstReference &Inst,
12971293 return make_gadget_report (RetKind, Inst, *RetReg);
12981294}
12991295
1296+ // / While BOLT already marks some of the branch instructions as tail calls,
1297+ // / this function tries to improve the coverage by including less obvious cases
1298+ // / when it is possible to do without introducing too many false positives.
1299+ static bool shouldAnalyzeTailCallInst (const BinaryContext &BC,
1300+ const BinaryFunction &BF,
1301+ const MCInstReference &Inst) {
1302+ // Some BC.MIB->isXYZ(Inst) methods simply delegate to MCInstrDesc::isXYZ()
1303+ // (such as isBranch at the time of writing this comment), some don't (such
1304+ // as isCall). For that reason, call MCInstrDesc's methods explicitly when
1305+ // it is important.
1306+ const MCInstrDesc &Desc =
1307+ BC.MII ->get (static_cast <const MCInst &>(Inst).getOpcode ());
1308+ // Tail call should be a branch (but not necessarily an indirect one).
1309+ if (!Desc.isBranch ())
1310+ return false ;
1311+
1312+ // Always analyze the branches already marked as tail calls by BOLT.
1313+ if (BC.MIB ->isTailCall (Inst))
1314+ return true ;
1315+
1316+ // Try to also check the branches marked as "UNKNOWN CONTROL FLOW" - the
1317+ // below is a simplified condition from BinaryContext::printInstruction.
1318+ bool IsUnknownControlFlow =
1319+ BC.MIB ->isIndirectBranch (Inst) && !BC.MIB ->getJumpTable (Inst);
1320+
1321+ if (BF.hasCFG () && IsUnknownControlFlow)
1322+ return true ;
1323+
1324+ return false ;
1325+ }
1326+
1327+ static std::optional<PartialReport<MCPhysReg>>
1328+ shouldReportUnsafeTailCall (const BinaryContext &BC, const BinaryFunction &BF,
1329+ const MCInstReference &Inst, const SrcState &S) {
1330+ static const GadgetKind UntrustedLRKind (
1331+ " untrusted link register found before tail call" );
1332+
1333+ if (!shouldAnalyzeTailCallInst (BC, BF, Inst))
1334+ return std::nullopt ;
1335+
1336+ // Not only the set of registers returned by getTrustedLiveInRegs() can be
1337+ // seen as a reasonable target-independent _approximation_ of "the LR", these
1338+ // are *exactly* those registers used by SrcSafetyAnalysis to initialize the
1339+ // set of trusted registers on function entry.
1340+ // Thus, this function basically checks that the precondition expected to be
1341+ // imposed by a function call instruction (which is hardcoded into the target-
1342+ // specific getTrustedLiveInRegs() function) is also respected on tail calls.
1343+ SmallVector<MCPhysReg> RegsToCheck = BC.MIB ->getTrustedLiveInRegs ();
1344+ LLVM_DEBUG ({
1345+ traceInst (BC, " Found tail call inst" , Inst);
1346+ traceRegMask (BC, " Trusted regs" , S.TrustedRegs );
1347+ });
1348+
1349+ // In musl on AArch64, the _start function sets LR to zero and calls the next
1350+ // stage initialization function at the end, something along these lines:
1351+ //
1352+ // _start:
1353+ // mov x30, #0
1354+ // ; ... other initialization ...
1355+ // b _start_c ; performs "exit" system call at some point
1356+ //
1357+ // As this would produce a false positive for every executable linked with
1358+ // such libc, ignore tail calls performed by ELF entry function.
1359+ if (BC.StartFunctionAddress &&
1360+ *BC.StartFunctionAddress == Inst.getFunction ()->getAddress ()) {
1361+ LLVM_DEBUG ({ dbgs () << " Skipping tail call in ELF entry function.\n " ; });
1362+ return std::nullopt ;
1363+ }
1364+
1365+ // Returns at most one report per instruction - this is probably OK...
1366+ for (auto Reg : RegsToCheck)
1367+ if (!S.TrustedRegs [Reg])
1368+ return make_gadget_report (UntrustedLRKind, Inst, Reg);
1369+
1370+ return std::nullopt ;
1371+ }
1372+
13001373static std::optional<PartialReport<MCPhysReg>>
13011374shouldReportCallGadget (const BinaryContext &BC, const MCInstReference &Inst,
13021375 const SrcState &S) {
@@ -1452,6 +1525,9 @@ void FunctionAnalysisContext::findUnsafeUses(
14521525 if (PacRetGadgetsOnly)
14531526 return ;
14541527
1528+ if (auto Report = shouldReportUnsafeTailCall (BC, BF, Inst, S))
1529+ Reports.push_back (*Report);
1530+
14551531 if (auto Report = shouldReportCallGadget (BC, Inst, S))
14561532 Reports.push_back (*Report);
14571533 if (auto Report = shouldReportSigningOracle (BC, Inst, S))
0 commit comments