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