@@ -1319,6 +1319,90 @@ shouldReportReturnGadget(const BinaryContext &BC, const MCInstReference &Inst,
13191319 return make_gadget_report (RetKind, Inst, *RetReg);
13201320}
13211321
1322+ // / While BOLT already marks some of the branch instructions as tail calls,
1323+ // / this function tries to detect less obvious cases, assuming false positives
1324+ // / are acceptable as long as there are not too many of them.
1325+ // /
1326+ // / It is possible that not all the instructions classified as tail calls by
1327+ // / this function are safe to be considered as such for the purpose of code
1328+ // / transformations performed by BOLT. The intention of this function is to
1329+ // / spot some of actually missed tail calls (and likely a number of unrelated
1330+ // / indirect branch instructions) as long as this doesn't increase the amount
1331+ // / of false positive reports unacceptably.
1332+ static bool shouldAnalyzeTailCallInst (const BinaryContext &BC,
1333+ const BinaryFunction &BF,
1334+ const MCInstReference &Inst) {
1335+ // Some BC.MIB->isXYZ(Inst) methods simply delegate to MCInstrDesc::isXYZ()
1336+ // (such as isBranch at the time of writing this comment), some don't (such
1337+ // as isCall). For that reason, call MCInstrDesc's methods explicitly when
1338+ // it is important.
1339+ const MCInstrDesc &Desc =
1340+ BC.MII ->get (static_cast <const MCInst &>(Inst).getOpcode ());
1341+ // Tail call should be a branch (but not necessarily an indirect one).
1342+ if (!Desc.isBranch ())
1343+ return false ;
1344+
1345+ // Always analyze the branches already marked as tail calls by BOLT.
1346+ if (BC.MIB ->isTailCall (Inst))
1347+ return true ;
1348+
1349+ // Try to also check the branches marked as "UNKNOWN CONTROL FLOW" - the
1350+ // below is a simplified condition from BinaryContext::printInstruction.
1351+ bool IsUnknownControlFlow =
1352+ BC.MIB ->isIndirectBranch (Inst) && !BC.MIB ->getJumpTable (Inst);
1353+
1354+ if (BF.hasCFG () && IsUnknownControlFlow)
1355+ return true ;
1356+
1357+ return false ;
1358+ }
1359+
1360+ static std::optional<PartialReport<MCPhysReg>>
1361+ shouldReportUnsafeTailCall (const BinaryContext &BC, const BinaryFunction &BF,
1362+ const MCInstReference &Inst, const SrcState &S) {
1363+ static const GadgetKind UntrustedLRKind (
1364+ " untrusted link register found before tail call" );
1365+
1366+ if (!shouldAnalyzeTailCallInst (BC, BF, Inst))
1367+ return std::nullopt ;
1368+
1369+ // Not only the set of registers returned by getTrustedLiveInRegs() can be
1370+ // seen as a reasonable target-independent _approximation_ of "the LR", these
1371+ // are *exactly* those registers used by SrcSafetyAnalysis to initialize the
1372+ // set of trusted registers on function entry.
1373+ // Thus, this function basically checks that the precondition expected to be
1374+ // imposed by a function call instruction (which is hardcoded into the target-
1375+ // specific getTrustedLiveInRegs() function) is also respected on tail calls.
1376+ SmallVector<MCPhysReg> RegsToCheck = BC.MIB ->getTrustedLiveInRegs ();
1377+ LLVM_DEBUG ({
1378+ traceInst (BC, " Found tail call inst" , Inst);
1379+ traceRegMask (BC, " Trusted regs" , S.TrustedRegs );
1380+ });
1381+
1382+ // In musl on AArch64, the _start function sets LR to zero and calls the next
1383+ // stage initialization function at the end, something along these lines:
1384+ //
1385+ // _start:
1386+ // mov x30, #0
1387+ // ; ... other initialization ...
1388+ // b _start_c ; performs "exit" system call at some point
1389+ //
1390+ // As this would produce a false positive for every executable linked with
1391+ // such libc, ignore tail calls performed by ELF entry function.
1392+ if (BC.StartFunctionAddress &&
1393+ *BC.StartFunctionAddress == Inst.getFunction ()->getAddress ()) {
1394+ LLVM_DEBUG ({ dbgs () << " Skipping tail call in ELF entry function.\n " ; });
1395+ return std::nullopt ;
1396+ }
1397+
1398+ // Returns at most one report per instruction - this is probably OK...
1399+ for (auto Reg : RegsToCheck)
1400+ if (!S.TrustedRegs [Reg])
1401+ return make_gadget_report (UntrustedLRKind, Inst, Reg);
1402+
1403+ return std::nullopt ;
1404+ }
1405+
13221406static std::optional<PartialReport<MCPhysReg>>
13231407shouldReportCallGadget (const BinaryContext &BC, const MCInstReference &Inst,
13241408 const SrcState &S) {
@@ -1478,6 +1562,9 @@ void FunctionAnalysisContext::findUnsafeUses(
14781562 if (PacRetGadgetsOnly)
14791563 return ;
14801564
1565+ if (auto Report = shouldReportUnsafeTailCall (BC, BF, Inst, S))
1566+ Reports.push_back (*Report);
1567+
14811568 if (auto Report = shouldReportCallGadget (BC, Inst, S))
14821569 Reports.push_back (*Report);
14831570 if (auto Report = shouldReportSigningOracle (BC, Inst, S))
0 commit comments