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 << " : " ;
@@ -363,6 +369,34 @@ class SrcSafetyAnalysis {
363369 return Clobbered;
364370 }
365371
372+ std::optional<MCPhysReg> getRegMadeTrustedByChecking (const MCInst &Inst,
373+ SrcState Cur) const {
374+ // This functions cannot return multiple registers. This is never the case
375+ // on AArch64.
376+ std::optional<MCPhysReg> RegCheckedByInst =
377+ BC.MIB ->getAuthCheckedReg (Inst, /* MayOverwrite=*/ false );
378+ if (RegCheckedByInst && Cur.SafeToDerefRegs [*RegCheckedByInst])
379+ return *RegCheckedByInst;
380+
381+ auto It = CheckerSequenceInfo.find (&Inst);
382+ if (It == CheckerSequenceInfo.end ())
383+ return std::nullopt ;
384+
385+ MCPhysReg RegCheckedBySequence = It->second .first ;
386+ const MCInst *FirstCheckerInst = It->second .second ;
387+
388+ // FirstCheckerInst should belong to the same basic block (see the
389+ // assertion in DataflowSrcSafetyAnalysis::run()), meaning it was
390+ // deterministically processed a few steps before this instruction.
391+ const SrcState &StateBeforeChecker = getStateBefore (*FirstCheckerInst);
392+
393+ // The sequence checks the register, but it should be authenticated before.
394+ if (!StateBeforeChecker.SafeToDerefRegs [RegCheckedBySequence])
395+ return std::nullopt ;
396+
397+ return RegCheckedBySequence;
398+ }
399+
366400 // Returns all registers that can be treated as if they are written by an
367401 // authentication instruction.
368402 SmallVector<MCPhysReg> getRegsMadeSafeToDeref (const MCInst &Point,
@@ -385,18 +419,38 @@ class SrcSafetyAnalysis {
385419 Regs.push_back (DstAndSrc->first );
386420 }
387421
422+ // Make sure explicit checker sequence keeps register safe-to-dereference
423+ // when the register would be clobbered according to the regular rules:
424+ //
425+ // ; LR is safe to dereference here
426+ // mov x16, x30 ; start of the sequence, LR is s-t-d right before
427+ // xpaclri ; clobbers LR, LR is not safe anymore
428+ // cmp x30, x16
429+ // b.eq 1f ; end of the sequence: LR is marked as trusted
430+ // brk 0x1234
431+ // 1:
432+ // ; at this point LR would be marked as trusted,
433+ // ; but not safe-to-dereference
434+ //
435+ // or even just
436+ //
437+ // ; X1 is safe to dereference here
438+ // ldr x0, [x1, #8]!
439+ // ; X1 is trusted here, but it was clobbered due to address write-back
440+ if (auto CheckedReg = getRegMadeTrustedByChecking (Point, Cur))
441+ Regs.push_back (*CheckedReg);
442+
388443 return Regs;
389444 }
390445
391446 // Returns all registers made trusted by this instruction.
392447 SmallVector<MCPhysReg> getRegsMadeTrusted (const MCInst &Point,
393448 const SrcState &Cur) const {
449+ assert (!AuthTrapsOnFailure && " Use getRegsMadeSafeToDeref instead" );
394450 SmallVector<MCPhysReg> Regs;
395451
396452 // An authenticated pointer can be checked, or
397- std::optional<MCPhysReg> CheckedReg =
398- BC.MIB ->getAuthCheckedReg (Point, /* MayOverwrite=*/ false );
399- if (CheckedReg && Cur.SafeToDerefRegs [*CheckedReg])
453+ if (auto CheckedReg = getRegMadeTrustedByChecking (Point, Cur))
400454 Regs.push_back (*CheckedReg);
401455
402456 // ... a pointer can be authenticated by an instruction that always checks
@@ -407,19 +461,6 @@ class SrcSafetyAnalysis {
407461 if (AutReg && IsChecked)
408462 Regs.push_back (*AutReg);
409463
410- if (CheckerSequenceInfo.contains (&Point)) {
411- MCPhysReg CheckedReg;
412- const MCInst *FirstCheckerInst;
413- std::tie (CheckedReg, FirstCheckerInst) = CheckerSequenceInfo.at (&Point);
414-
415- // FirstCheckerInst should belong to the same basic block (see the
416- // assertion in DataflowSrcSafetyAnalysis::run()), meaning it was
417- // deterministically processed a few steps before this instruction.
418- const SrcState &StateBeforeChecker = getStateBefore (*FirstCheckerInst);
419- if (StateBeforeChecker.SafeToDerefRegs [CheckedReg])
420- Regs.push_back (CheckedReg);
421- }
422-
423464 // ... a safe address can be materialized, or
424465 if (auto NewAddrReg = BC.MIB ->getMaterializedAddressRegForPtrAuth (Point))
425466 Regs.push_back (*NewAddrReg);
@@ -462,28 +503,11 @@ class SrcSafetyAnalysis {
462503 BitVector Clobbered = getClobberedRegs (Point);
463504 SmallVector<MCPhysReg> NewSafeToDerefRegs =
464505 getRegsMadeSafeToDeref (Point, Cur);
465- SmallVector<MCPhysReg> NewTrustedRegs = getRegsMadeTrusted (Point, Cur);
466-
467- // Ideally, being trusted is a strictly stronger property than being
468- // safe-to-dereference. To simplify the computation of Next state, enforce
469- // this for NewSafeToDerefRegs and NewTrustedRegs. Additionally, this
470- // fixes the properly for "cumulative" register states in tricky cases
471- // like the following:
472- //
473- // ; LR is safe to dereference here
474- // mov x16, x30 ; start of the sequence, LR is s-t-d right before
475- // xpaclri ; clobbers LR, LR is not safe anymore
476- // cmp x30, x16
477- // b.eq 1f ; end of the sequence: LR is marked as trusted
478- // brk 0x1234
479- // 1:
480- // ; at this point LR would be marked as trusted,
481- // ; but not safe-to-dereference
482- //
483- for (auto TrustedReg : NewTrustedRegs) {
484- if (!is_contained (NewSafeToDerefRegs, TrustedReg))
485- NewSafeToDerefRegs.push_back (TrustedReg);
486- }
506+ // If authentication instructions trap on failure, safe-to-dereference
507+ // registers are always trusted.
508+ SmallVector<MCPhysReg> NewTrustedRegs =
509+ AuthTrapsOnFailure ? NewSafeToDerefRegs
510+ : getRegsMadeTrusted (Point, Cur);
487511
488512 // Then, compute the state after this instruction is executed.
489513 SrcState Next = Cur;
@@ -520,6 +544,11 @@ class SrcSafetyAnalysis {
520544 dbgs () << " )\n " ;
521545 });
522546
547+ // Being trusted is a strictly stronger property than being
548+ // safe-to-dereference.
549+ assert (!Next.TrustedRegs .test (Next.SafeToDerefRegs ) &&
550+ " SafeToDerefRegs should contain all TrustedRegs" );
551+
523552 return Next;
524553 }
525554
@@ -1106,6 +1135,11 @@ class DataflowDstSafetyAnalysis
11061135 }
11071136
11081137 void run () override {
1138+ // As long as DstSafetyAnalysis is only computed to detect authentication
1139+ // oracles, it is a waste of time to compute it when authentication
1140+ // instructions are known to always trap on failure.
1141+ assert (!AuthTrapsOnFailure &&
1142+ " DstSafetyAnalysis is useless with faulting auth" );
11091143 for (BinaryBasicBlock &BB : Func) {
11101144 if (auto CheckerInfo = BC.MIB ->getAuthCheckedReg (BB)) {
11111145 LLVM_DEBUG ({
@@ -1550,6 +1584,8 @@ void FunctionAnalysisContext::findUnsafeDefs(
15501584 SmallVector<PartialReport<MCPhysReg>> &Reports) {
15511585 if (PacRetGadgetsOnly)
15521586 return ;
1587+ if (AuthTrapsOnFailure)
1588+ return ;
15531589
15541590 auto Analysis = DstSafetyAnalysis::create (BF, AllocatorId, {});
15551591 LLVM_DEBUG ({ dbgs () << " Running dst register safety analysis...\n " ; });
0 commit comments