@@ -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,57 +376,81 @@ class PacRetAnalysis
376376 }
377377};
378378
379+ static std::shared_ptr<Report> tryCheckReturn (const BinaryContext &BC,
380+ 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,
411+ Analysis::computeDfState (BinaryFunction &BF,
381412 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 = tryCheckReturn (BC, Inst, S))
429+ Result.Diagnostics .push_back (Report);
428430 }
429431 }
432+
433+ if (Result.Diagnostics .empty ())
434+ return Result;
435+
436+ // Redo the analysis, but now also track which instructions last wrote
437+ // to any of the registers in RetRegsWithGadgets, so that better
438+ // diagnostics can be produced.
439+
440+ SmallSet<MCPhysReg, 4 > RegsToTrack;
441+ for (auto Report : Result.Diagnostics )
442+ for (MCPhysReg Reg : Report->getAffectedRegisters ())
443+ RegsToTrack.insert (Reg);
444+
445+ std::vector<MCPhysReg> RegsToTrackVec (RegsToTrack.begin (), RegsToTrack.end ());
446+
447+ PacRetAnalysis PRWIA (BF, AllocatorId, RegsToTrackVec);
448+ PRWIA.run ();
449+ for (auto Report : Result.Diagnostics ) {
450+ Report->setOverwritingInstrs (PRWIA.getLastClobberingInsts (
451+ Report->Location , BF, Report->getAffectedRegisters ()));
452+ }
453+
430454 return Result;
431455}
432456
@@ -438,27 +462,19 @@ void Analysis::runOnFunction(BinaryFunction &BF,
438462 BF.dump ();
439463 });
440464
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- }
465+ if (!BF.hasCFG ())
466+ return ;
454467
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- }
468+ FunctionAnalysisResult FAR = computeDfState (BF, AllocatorId);
469+ if (FAR.Diagnostics .empty ())
470+ return ;
471+
472+ // `runOnFunction` is typically getting called from multiple threads in
473+ // parallel. Therefore, use a lock to avoid data races when storing the
474+ // result of the analysis in the `AnalysisResults` map.
475+ {
476+ std::lock_guard<std::mutex> Lock (AnalysisResultsMutex);
477+ AnalysisResults[&BF] = FAR;
462478 }
463479}
464480
0 commit comments