@@ -353,7 +353,7 @@ class PacRetAnalysis
353353public:
354354 std::vector<MCInstReference>
355355 getLastClobberingInsts (const MCInst Ret, BinaryFunction &BF,
356- const BitVector & UsedDirtyRegs) const {
356+ const ArrayRef<MCPhysReg> UsedDirtyRegs) const {
357357 if (RegsToTrackInstsFor.empty ())
358358 return {};
359359 auto MaybeState = getStateAt (Ret);
@@ -362,7 +362,7 @@ class PacRetAnalysis
362362 const State &S = *MaybeState;
363363 // Due to aliasing registers, multiple registers may have been tracked.
364364 std::set<const MCInst *> LastWritingInsts;
365- for (MCPhysReg TrackedReg : UsedDirtyRegs. set_bits () ) {
365+ for (MCPhysReg TrackedReg : UsedDirtyRegs) {
366366 for (const MCInst *Inst : lastWritingInsts (S, TrackedReg))
367367 LastWritingInsts.insert (Inst);
368368 }
@@ -376,60 +376,91 @@ class PacRetAnalysis
376376 }
377377};
378378
379+ static std::shared_ptr<Report>
380+ shouldReportReturnGadget (const BinaryContext &BC, const MCInstReference &Inst,
381+ const State &S) {
382+ static const GadgetKind RetKind (" non-protected ret found" );
383+ if (!BC.MIB ->isReturn (Inst))
384+ return nullptr ;
385+
386+ ErrorOr<MCPhysReg> MaybeRetReg = BC.MIB ->getRegUsedAsRetDest (Inst);
387+ if (MaybeRetReg.getError ()) {
388+ return std::make_shared<GenericReport>(
389+ Inst, " Warning: pac-ret analysis could not analyze this return "
390+ " instruction" );
391+ }
392+ MCPhysReg RetReg = *MaybeRetReg;
393+ LLVM_DEBUG ({
394+ traceInst (BC, " Found RET inst" , Inst);
395+ traceReg (BC, " RetReg" , RetReg);
396+ traceReg (BC, " Authenticated reg" , BC.MIB ->getAuthenticatedReg (Inst));
397+ });
398+ if (BC.MIB ->isAuthenticationOfReg (Inst, RetReg))
399+ return nullptr ;
400+ BitVector UsedDirtyRegs = S.NonAutClobRegs ;
401+ LLVM_DEBUG ({ traceRegMask (BC, " NonAutClobRegs at Ret" , UsedDirtyRegs); });
402+ UsedDirtyRegs &= BC.MIB ->getAliases (RetReg, /* OnlySmaller=*/ true );
403+ LLVM_DEBUG ({ traceRegMask (BC, " Intersection with RetReg" , UsedDirtyRegs); });
404+ if (!UsedDirtyRegs.any ())
405+ return nullptr ;
406+
407+ return std::make_shared<GadgetReport>(RetKind, Inst, UsedDirtyRegs);
408+ }
409+
379410FunctionAnalysisResult
380- Analysis::computeDfState (PacRetAnalysis &PRA, BinaryFunction &BF,
381- MCPlusBuilder::AllocatorIdTy AllocatorId) {
411+ Analysis::findGadgets (BinaryFunction &BF,
412+ MCPlusBuilder::AllocatorIdTy AllocatorId) {
413+ FunctionAnalysisResult Result;
414+
415+ PacRetAnalysis PRA (BF, AllocatorId, {});
382416 PRA.run ();
383417 LLVM_DEBUG ({
384418 dbgs () << " After PacRetAnalysis:\n " ;
385419 BF.dump ();
386420 });
387421
388- FunctionAnalysisResult Result;
389- // Now scan the CFG for non-authenticating return instructions that use an
390- // overwritten, non-authenticated register as return address.
391422 BinaryContext &BC = BF.getBinaryContext ();
392423 for (BinaryBasicBlock &BB : BF) {
393- for (int64_t I = BB.size () - 1 ; I >= 0 ; --I) {
394- MCInst &Inst = BB.getInstructionAtIndex (I);
395- if (BC.MIB ->isReturn (Inst)) {
396- ErrorOr<MCPhysReg> MaybeRetReg = BC.MIB ->getRegUsedAsRetDest (Inst);
397- if (MaybeRetReg.getError ()) {
398- Result.Diagnostics .push_back (std::make_shared<GenericReport>(
399- MCInstInBBReference (&BB, I),
400- " Warning: pac-ret analysis could not analyze this return "
401- " instruction" ));
402- continue ;
403- }
404- MCPhysReg RetReg = *MaybeRetReg;
405- LLVM_DEBUG ({
406- traceInst (BC, " Found RET inst" , Inst);
407- traceReg (BC, " RetReg" , RetReg);
408- traceReg (BC, " Authenticated reg" , BC.MIB ->getAuthenticatedReg (Inst));
409- });
410- if (BC.MIB ->isAuthenticationOfReg (Inst, RetReg))
411- break ;
412- BitVector UsedDirtyRegs = PRA.getStateAt (Inst)->NonAutClobRegs ;
413- LLVM_DEBUG (
414- { traceRegMask (BC, " NonAutClobRegs at Ret" , UsedDirtyRegs); });
415- UsedDirtyRegs &= BC.MIB ->getAliases (RetReg, /* OnlySmaller=*/ true );
416- LLVM_DEBUG (
417- { traceRegMask (BC, " Intersection with RetReg" , UsedDirtyRegs); });
418- if (UsedDirtyRegs.any ()) {
419- static const GadgetKind RetKind (" non-protected ret found" );
420- // This return instruction needs to be reported
421- Result.Diagnostics .push_back (std::make_shared<GadgetReport>(
422- RetKind, MCInstInBBReference (&BB, I),
423- PRA.getLastClobberingInsts (Inst, BF, UsedDirtyRegs)));
424- for (MCPhysReg RetRegWithGadget : UsedDirtyRegs.set_bits ())
425- Result.RegistersAffected .insert (RetRegWithGadget);
426- }
427- }
424+ for (int64_t I = 0 , E = BB.size (); I < E; ++I) {
425+ MCInstReference Inst (&BB, I);
426+ const State &S = *PRA.getStateAt (Inst);
427+
428+ if (auto Report = shouldReportReturnGadget (BC, Inst, S))
429+ Result.Diagnostics .push_back (Report);
428430 }
429431 }
430432 return Result;
431433}
432434
435+ void Analysis::computeDetailedInfo (BinaryFunction &BF,
436+ MCPlusBuilder::AllocatorIdTy AllocatorId,
437+ FunctionAnalysisResult &Result) {
438+ BinaryContext &BC = BF.getBinaryContext ();
439+
440+ // Collect the affected registers across all gadgets found in this function.
441+ SmallSet<MCPhysReg, 4 > RegsToTrack;
442+ for (auto Report : Result.Diagnostics )
443+ RegsToTrack.insert_range (Report->getAffectedRegisters ());
444+ std::vector<MCPhysReg> RegsToTrackVec (RegsToTrack.begin (), RegsToTrack.end ());
445+
446+ // Re-compute the analysis with register tracking.
447+ PacRetAnalysis PRWIA (BF, AllocatorId, RegsToTrackVec);
448+ PRWIA.run ();
449+ LLVM_DEBUG ({
450+ dbgs () << " After detailed PacRetAnalysis:\n " ;
451+ BF.dump ();
452+ });
453+
454+ // Augment gadget reports.
455+ for (auto Report : Result.Diagnostics ) {
456+ LLVM_DEBUG (
457+ { traceInst (BC, " Attaching clobbering info to" , Report->Location ); });
458+ (void )BC;
459+ Report->setOverwritingInstrs (PRWIA.getLastClobberingInsts (
460+ Report->Location , BF, Report->getAffectedRegisters ()));
461+ }
462+ }
463+
433464void Analysis::runOnFunction (BinaryFunction &BF,
434465 MCPlusBuilder::AllocatorIdTy AllocatorId) {
435466 LLVM_DEBUG ({
@@ -438,27 +469,25 @@ void Analysis::runOnFunction(BinaryFunction &BF,
438469 BF.dump ();
439470 });
440471
441- if (BF.hasCFG ()) {
442- PacRetAnalysis PRA (BF, AllocatorId, {});
443- FunctionAnalysisResult FAR = computeDfState (PRA, BF, AllocatorId);
444- if (!FAR.RegistersAffected .empty ()) {
445- // Redo the analysis, but now also track which instructions last wrote
446- // to any of the registers in RetRegsWithGadgets, so that better
447- // diagnostics can be produced.
448- std::vector<MCPhysReg> RegsToTrack;
449- for (MCPhysReg R : FAR.RegistersAffected )
450- RegsToTrack.push_back (R);
451- PacRetAnalysis PRWIA (BF, AllocatorId, RegsToTrack);
452- FAR = computeDfState (PRWIA, BF, AllocatorId);
453- }
472+ if (!BF.hasCFG ())
473+ return ;
454474
455- // `runOnFunction` is typically getting called from multiple threads in
456- // parallel. Therefore, use a lock to avoid data races when storing the
457- // result of the analysis in the `AnalysisResults` map.
458- {
459- std::lock_guard<std::mutex> Lock (AnalysisResultsMutex);
460- AnalysisResults[&BF] = FAR;
461- }
475+ FunctionAnalysisResult FAR = findGadgets (BF, AllocatorId);
476+ if (FAR.Diagnostics .empty ())
477+ return ;
478+
479+ // Redo the analysis, but now also track which instructions last wrote
480+ // to any of the registers in RetRegsWithGadgets, so that better
481+ // diagnostics can be produced.
482+
483+ computeDetailedInfo (BF, AllocatorId, FAR);
484+
485+ // `runOnFunction` is typically getting called from multiple threads in
486+ // parallel. Therefore, use a lock to avoid data races when storing the
487+ // result of the analysis in the `AnalysisResults` map.
488+ {
489+ std::lock_guard<std::mutex> Lock (AnalysisResultsMutex);
490+ AnalysisResults[&BF] = FAR;
462491 }
463492}
464493
@@ -517,7 +546,7 @@ void GadgetReport::generateReport(raw_ostream &OS,
517546 << " instructions that write to the affected registers after any "
518547 " authentication are:\n " ;
519548 // Sort by address to ensure output is deterministic.
520- std::vector <MCInstReference> OI = OverwritingInstrs;
549+ SmallVector <MCInstReference> OI = OverwritingInstrs;
521550 llvm::sort (OI, [](const MCInstReference &A, const MCInstReference &B) {
522551 return A.getAddress () < B.getAddress ();
523552 });
0 commit comments