1414#include " bolt/Passes/PAuthGadgetScanner.h"
1515#include " bolt/Core/ParallelUtilities.h"
1616#include " bolt/Passes/DataflowAnalysis.h"
17+ #include " bolt/Utils/CommandLineOpts.h"
1718#include " llvm/ADT/STLExtras.h"
1819#include " llvm/ADT/SmallSet.h"
1920#include " llvm/MC/MCInst.h"
@@ -26,6 +27,11 @@ namespace llvm {
2627namespace bolt {
2728namespace PAuthGadgetScanner {
2829
30+ static cl::opt<bool > AuthTrapsOnFailure (
31+ " auth-traps-on-failure" ,
32+ cl::desc (" Assume authentication instructions always trap on failure" ),
33+ cl::cat(opts::BinaryAnalysisCategory));
34+
2935[[maybe_unused]] static void traceInst (const BinaryContext &BC, StringRef Label,
3036 const MCInst &MI) {
3137 dbgs () << " " << Label << " : " ;
@@ -365,6 +371,34 @@ class SrcSafetyAnalysis {
365371 return Clobbered;
366372 }
367373
374+ std::optional<MCPhysReg> getRegMadeTrustedByChecking (const MCInst &Inst,
375+ SrcState Cur) const {
376+ // This functions cannot return multiple registers. This is never the case
377+ // on AArch64.
378+ std::optional<MCPhysReg> RegCheckedByInst =
379+ BC.MIB ->getAuthCheckedReg (Inst, /* MayOverwrite=*/ false );
380+ if (RegCheckedByInst && Cur.SafeToDerefRegs [*RegCheckedByInst])
381+ return *RegCheckedByInst;
382+
383+ auto It = CheckerSequenceInfo.find (&Inst);
384+ if (It == CheckerSequenceInfo.end ())
385+ return std::nullopt ;
386+
387+ MCPhysReg RegCheckedBySequence = It->second .first ;
388+ const MCInst *FirstCheckerInst = It->second .second ;
389+
390+ // FirstCheckerInst should belong to the same basic block (see the
391+ // assertion in DataflowSrcSafetyAnalysis::run()), meaning it was
392+ // deterministically processed a few steps before this instruction.
393+ const SrcState &StateBeforeChecker = getStateBefore (*FirstCheckerInst);
394+
395+ // The sequence checks the register, but it should be authenticated before.
396+ if (!StateBeforeChecker.SafeToDerefRegs [RegCheckedBySequence])
397+ return std::nullopt ;
398+
399+ return RegCheckedBySequence;
400+ }
401+
368402 // Returns all registers that can be treated as if they are written by an
369403 // authentication instruction.
370404 SmallVector<MCPhysReg> getRegsMadeSafeToDeref (const MCInst &Point,
@@ -387,18 +421,38 @@ class SrcSafetyAnalysis {
387421 Regs.push_back (DstAndSrc->first );
388422 }
389423
424+ // Make sure explicit checker sequence keeps register safe-to-dereference
425+ // when the register would be clobbered according to the regular rules:
426+ //
427+ // ; LR is safe to dereference here
428+ // mov x16, x30 ; start of the sequence, LR is s-t-d right before
429+ // xpaclri ; clobbers LR, LR is not safe anymore
430+ // cmp x30, x16
431+ // b.eq 1f ; end of the sequence: LR is marked as trusted
432+ // brk 0x1234
433+ // 1:
434+ // ; at this point LR would be marked as trusted,
435+ // ; but not safe-to-dereference
436+ //
437+ // or even just
438+ //
439+ // ; X1 is safe to dereference here
440+ // ldr x0, [x1, #8]!
441+ // ; X1 is trusted here, but it was clobbered due to address write-back
442+ if (auto CheckedReg = getRegMadeTrustedByChecking (Point, Cur))
443+ Regs.push_back (*CheckedReg);
444+
390445 return Regs;
391446 }
392447
393448 // Returns all registers made trusted by this instruction.
394449 SmallVector<MCPhysReg> getRegsMadeTrusted (const MCInst &Point,
395450 const SrcState &Cur) const {
451+ assert (!AuthTrapsOnFailure && " Use getRegsMadeSafeToDeref instead" );
396452 SmallVector<MCPhysReg> Regs;
397453
398454 // An authenticated pointer can be checked, or
399- std::optional<MCPhysReg> CheckedReg =
400- BC.MIB ->getAuthCheckedReg (Point, /* MayOverwrite=*/ false );
401- if (CheckedReg && Cur.SafeToDerefRegs [*CheckedReg])
455+ if (auto CheckedReg = getRegMadeTrustedByChecking (Point, Cur))
402456 Regs.push_back (*CheckedReg);
403457
404458 // ... a pointer can be authenticated by an instruction that always checks
@@ -409,19 +463,6 @@ class SrcSafetyAnalysis {
409463 if (AutReg && IsChecked)
410464 Regs.push_back (*AutReg);
411465
412- if (CheckerSequenceInfo.contains (&Point)) {
413- MCPhysReg CheckedReg;
414- const MCInst *FirstCheckerInst;
415- std::tie (CheckedReg, FirstCheckerInst) = CheckerSequenceInfo.at (&Point);
416-
417- // FirstCheckerInst should belong to the same basic block (see the
418- // assertion in DataflowSrcSafetyAnalysis::run()), meaning it was
419- // deterministically processed a few steps before this instruction.
420- const SrcState &StateBeforeChecker = getStateBefore (*FirstCheckerInst);
421- if (StateBeforeChecker.SafeToDerefRegs [CheckedReg])
422- Regs.push_back (CheckedReg);
423- }
424-
425466 // ... a safe address can be materialized, or
426467 if (auto NewAddrReg = BC.MIB ->getMaterializedAddressRegForPtrAuth (Point))
427468 Regs.push_back (*NewAddrReg);
@@ -465,28 +506,11 @@ class SrcSafetyAnalysis {
465506 BitVector Clobbered = getClobberedRegs (Point);
466507 SmallVector<MCPhysReg> NewSafeToDerefRegs =
467508 getRegsMadeSafeToDeref (Point, Cur);
468- SmallVector<MCPhysReg> NewTrustedRegs = getRegsMadeTrusted (Point, Cur);
469-
470- // Ideally, being trusted is a strictly stronger property than being
471- // safe-to-dereference. To simplify the computation of Next state, enforce
472- // this for NewSafeToDerefRegs and NewTrustedRegs. Additionally, this
473- // fixes the properly for "cumulative" register states in tricky cases
474- // like the following:
475- //
476- // ; LR is safe to dereference here
477- // mov x16, x30 ; start of the sequence, LR is s-t-d right before
478- // xpaclri ; clobbers LR, LR is not safe anymore
479- // cmp x30, x16
480- // b.eq 1f ; end of the sequence: LR is marked as trusted
481- // brk 0x1234
482- // 1:
483- // ; at this point LR would be marked as trusted,
484- // ; but not safe-to-dereference
485- //
486- for (auto TrustedReg : NewTrustedRegs) {
487- if (!is_contained (NewSafeToDerefRegs, TrustedReg))
488- NewSafeToDerefRegs.push_back (TrustedReg);
489- }
509+ // If authentication instructions trap on failure, safe-to-dereference
510+ // registers are always trusted.
511+ SmallVector<MCPhysReg> NewTrustedRegs =
512+ AuthTrapsOnFailure ? NewSafeToDerefRegs
513+ : getRegsMadeTrusted (Point, Cur);
490514
491515 // Then, compute the state after this instruction is executed.
492516 SrcState Next = Cur;
@@ -523,6 +547,11 @@ class SrcSafetyAnalysis {
523547 dbgs () << " )\n " ;
524548 });
525549
550+ // Being trusted is a strictly stronger property than being
551+ // safe-to-dereference.
552+ assert (!Next.TrustedRegs .test (Next.SafeToDerefRegs ) &&
553+ " SafeToDerefRegs should contain all TrustedRegs" );
554+
526555 return Next;
527556 }
528557
@@ -1084,6 +1113,11 @@ class DataflowDstSafetyAnalysis
10841113 }
10851114
10861115 void run () override {
1116+ // As long as DstSafetyAnalysis is only computed to detect authentication
1117+ // oracles, it is a waste of time to compute it when authentication
1118+ // instructions are known to always trap on failure.
1119+ assert (!AuthTrapsOnFailure &&
1120+ " DstSafetyAnalysis is useless with faulting auth" );
10871121 for (BinaryBasicBlock &BB : Func) {
10881122 if (auto CheckerInfo = BC.MIB ->getAuthCheckedReg (BB)) {
10891123 LLVM_DEBUG ({
@@ -1542,6 +1576,8 @@ void FunctionAnalysisContext::findUnsafeDefs(
15421576 SmallVector<PartialReport<MCPhysReg>> &Reports) {
15431577 if (PacRetGadgetsOnly)
15441578 return ;
1579+ if (AuthTrapsOnFailure)
1580+ return ;
15451581
15461582 auto Analysis = DstSafetyAnalysis::create (BF, AllocatorId, {});
15471583 LLVM_DEBUG ({ dbgs () << " Running dst register safety analysis...\n " ; });
0 commit comments