@@ -1319,6 +1319,83 @@ 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 improve the coverage by including less obvious cases
1324+ // / when it is possible to do without introducing too many false positives.
1325+ static  bool  shouldAnalyzeTailCallInst (const  BinaryContext &BC,
1326+                                       const  BinaryFunction &BF,
1327+                                       const  MCInstReference &Inst) {
1328+   //  Some BC.MIB->isXYZ(Inst) methods simply delegate to MCInstrDesc::isXYZ()
1329+   //  (such as isBranch at the time of writing this comment), some don't (such
1330+   //  as isCall). For that reason, call MCInstrDesc's methods explicitly when
1331+   //  it is important.
1332+   const  MCInstrDesc &Desc =
1333+       BC.MII ->get (static_cast <const  MCInst &>(Inst).getOpcode ());
1334+   //  Tail call should be a branch (but not necessarily an indirect one).
1335+   if  (!Desc.isBranch ())
1336+     return  false ;
1337+ 
1338+   //  Always analyze the branches already marked as tail calls by BOLT.
1339+   if  (BC.MIB ->isTailCall (Inst))
1340+     return  true ;
1341+ 
1342+   //  Try to also check the branches marked as "UNKNOWN CONTROL FLOW" - the
1343+   //  below is a simplified condition from BinaryContext::printInstruction.
1344+   bool  IsUnknownControlFlow =
1345+       BC.MIB ->isIndirectBranch (Inst) && !BC.MIB ->getJumpTable (Inst);
1346+ 
1347+   if  (BF.hasCFG () && IsUnknownControlFlow)
1348+     return  true ;
1349+ 
1350+   return  false ;
1351+ }
1352+ 
1353+ static  std::optional<PartialReport<MCPhysReg>>
1354+ shouldReportUnsafeTailCall (const  BinaryContext &BC, const  BinaryFunction &BF,
1355+                            const  MCInstReference &Inst, const  SrcState &S) {
1356+   static  const  GadgetKind UntrustedLRKind (
1357+       " untrusted link register found before tail call" 
1358+ 
1359+   if  (!shouldAnalyzeTailCallInst (BC, BF, Inst))
1360+     return  std::nullopt ;
1361+ 
1362+   //  Not only the set of registers returned by getTrustedLiveInRegs() can be
1363+   //  seen as a reasonable target-independent _approximation_ of "the LR", these
1364+   //  are *exactly* those registers used by SrcSafetyAnalysis to initialize the
1365+   //  set of trusted registers on function entry.
1366+   //  Thus, this function basically checks that the precondition expected to be
1367+   //  imposed by a function call instruction (which is hardcoded into the target-
1368+   //  specific getTrustedLiveInRegs() function) is also respected on tail calls.
1369+   SmallVector<MCPhysReg> RegsToCheck = BC.MIB ->getTrustedLiveInRegs ();
1370+   LLVM_DEBUG ({
1371+     traceInst (BC, " Found tail call inst" 
1372+     traceRegMask (BC, " Trusted regs" TrustedRegs );
1373+   });
1374+ 
1375+   //  In musl on AArch64, the _start function sets LR to zero and calls the next
1376+   //  stage initialization function at the end, something along these lines:
1377+   // 
1378+   //    _start:
1379+   //      mov     x30, #0
1380+   //      ; ... other initialization ...
1381+   //      b       _start_c ; performs "exit" system call at some point
1382+   // 
1383+   //  As this would produce a false positive for every executable linked with
1384+   //  such libc, ignore tail calls performed by ELF entry function.
1385+   if  (BC.StartFunctionAddress  &&
1386+       *BC.StartFunctionAddress  == Inst.getFunction ()->getAddress ()) {
1387+     LLVM_DEBUG ({ dbgs () << "   Skipping tail call in ELF entry function.\n " 
1388+     return  std::nullopt ;
1389+   }
1390+ 
1391+   //  Returns at most one report per instruction - this is probably OK...
1392+   for  (auto  Reg : RegsToCheck)
1393+     if  (!S.TrustedRegs [Reg])
1394+       return  make_gadget_report (UntrustedLRKind, Inst, Reg);
1395+ 
1396+   return  std::nullopt ;
1397+ }
1398+ 
13221399static  std::optional<PartialReport<MCPhysReg>>
13231400shouldReportCallGadget (const  BinaryContext &BC, const  MCInstReference &Inst,
13241401                       const  SrcState &S) {
@@ -1473,6 +1550,9 @@ void FunctionAnalysisContext::findUnsafeUses(
14731550    if  (PacRetGadgetsOnly)
14741551      return ;
14751552
1553+     if  (auto  Report = shouldReportUnsafeTailCall (BC, BF, Inst, S))
1554+       Reports.push_back (*Report);
1555+ 
14761556    if  (auto  Report = shouldReportCallGadget (BC, Inst, S))
14771557      Reports.push_back (*Report);
14781558    if  (auto  Report = shouldReportSigningOracle (BC, Inst, S))
0 commit comments