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 << " : " ;
@@ -332,6 +338,34 @@ class SrcSafetyAnalysis {
332338 return Clobbered;
333339 }
334340
341+ std::optional<MCPhysReg> getRegMadeTrustedByChecking (const MCInst &Inst,
342+ SrcState Cur) const {
343+ // This functions cannot return multiple registers. This is never the case
344+ // on AArch64.
345+ std::optional<MCPhysReg> RegCheckedByInst =
346+ BC.MIB ->getAuthCheckedReg (Inst, /* MayOverwrite=*/ false );
347+ if (RegCheckedByInst && Cur.SafeToDerefRegs [*RegCheckedByInst])
348+ return *RegCheckedByInst;
349+
350+ auto It = CheckerSequenceInfo.find (&Inst);
351+ if (It == CheckerSequenceInfo.end ())
352+ return std::nullopt ;
353+
354+ MCPhysReg RegCheckedBySequence = It->second .first ;
355+ const MCInst *FirstCheckerInst = It->second .second ;
356+
357+ // FirstCheckerInst should belong to the same basic block (see the
358+ // assertion in DataflowSrcSafetyAnalysis::run()), meaning it was
359+ // deterministically processed a few steps before this instruction.
360+ const SrcState &StateBeforeChecker = getStateBefore (*FirstCheckerInst);
361+
362+ // The sequence checks the register, but it should be authenticated before.
363+ if (!StateBeforeChecker.SafeToDerefRegs [RegCheckedBySequence])
364+ return std::nullopt ;
365+
366+ return RegCheckedBySequence;
367+ }
368+
335369 // Returns all registers that can be treated as if they are written by an
336370 // authentication instruction.
337371 SmallVector<MCPhysReg> getRegsMadeSafeToDeref (const MCInst &Point,
@@ -354,18 +388,38 @@ class SrcSafetyAnalysis {
354388 Regs.push_back (DstAndSrc->first );
355389 }
356390
391+ // Make sure explicit checker sequence keeps register safe-to-dereference
392+ // when the register would be clobbered according to the regular rules:
393+ //
394+ // ; LR is safe to dereference here
395+ // mov x16, x30 ; start of the sequence, LR is s-t-d right before
396+ // xpaclri ; clobbers LR, LR is not safe anymore
397+ // cmp x30, x16
398+ // b.eq 1f ; end of the sequence: LR is marked as trusted
399+ // brk 0x1234
400+ // 1:
401+ // ; at this point LR would be marked as trusted,
402+ // ; but not safe-to-dereference
403+ //
404+ // or even just
405+ //
406+ // ; X1 is safe to dereference here
407+ // ldr x0, [x1, #8]!
408+ // ; X1 is trusted here, but it was clobbered due to address write-back
409+ if (auto CheckedReg = getRegMadeTrustedByChecking (Point, Cur))
410+ Regs.push_back (*CheckedReg);
411+
357412 return Regs;
358413 }
359414
360415 // Returns all registers made trusted by this instruction.
361416 SmallVector<MCPhysReg> getRegsMadeTrusted (const MCInst &Point,
362417 const SrcState &Cur) const {
418+ assert (!AuthTrapsOnFailure && " Use getRegsMadeSafeToDeref instead" );
363419 SmallVector<MCPhysReg> Regs;
364420
365421 // An authenticated pointer can be checked, or
366- std::optional<MCPhysReg> CheckedReg =
367- BC.MIB ->getAuthCheckedReg (Point, /* MayOverwrite=*/ false );
368- if (CheckedReg && Cur.SafeToDerefRegs [*CheckedReg])
422+ if (auto CheckedReg = getRegMadeTrustedByChecking (Point, Cur))
369423 Regs.push_back (*CheckedReg);
370424
371425 // ... a pointer can be authenticated by an instruction that always checks
@@ -376,19 +430,6 @@ class SrcSafetyAnalysis {
376430 if (AutReg && IsChecked)
377431 Regs.push_back (*AutReg);
378432
379- if (CheckerSequenceInfo.contains (&Point)) {
380- MCPhysReg CheckedReg;
381- const MCInst *FirstCheckerInst;
382- std::tie (CheckedReg, FirstCheckerInst) = CheckerSequenceInfo.at (&Point);
383-
384- // FirstCheckerInst should belong to the same basic block (see the
385- // assertion in DataflowSrcSafetyAnalysis::run()), meaning it was
386- // deterministically processed a few steps before this instruction.
387- const SrcState &StateBeforeChecker = getStateBefore (*FirstCheckerInst);
388- if (StateBeforeChecker.SafeToDerefRegs [CheckedReg])
389- Regs.push_back (CheckedReg);
390- }
391-
392433 // ... a safe address can be materialized, or
393434 if (auto NewAddrReg = BC.MIB ->getMaterializedAddressRegForPtrAuth (Point))
394435 Regs.push_back (*NewAddrReg);
@@ -432,28 +473,11 @@ class SrcSafetyAnalysis {
432473 BitVector Clobbered = getClobberedRegs (Point);
433474 SmallVector<MCPhysReg> NewSafeToDerefRegs =
434475 getRegsMadeSafeToDeref (Point, Cur);
435- SmallVector<MCPhysReg> NewTrustedRegs = getRegsMadeTrusted (Point, Cur);
436-
437- // Ideally, being trusted is a strictly stronger property than being
438- // safe-to-dereference. To simplify the computation of Next state, enforce
439- // this for NewSafeToDerefRegs and NewTrustedRegs. Additionally, this
440- // fixes the properly for "cumulative" register states in tricky cases
441- // like the following:
442- //
443- // ; LR is safe to dereference here
444- // mov x16, x30 ; start of the sequence, LR is s-t-d right before
445- // xpaclri ; clobbers LR, LR is not safe anymore
446- // cmp x30, x16
447- // b.eq 1f ; end of the sequence: LR is marked as trusted
448- // brk 0x1234
449- // 1:
450- // ; at this point LR would be marked as trusted,
451- // ; but not safe-to-dereference
452- //
453- for (auto TrustedReg : NewTrustedRegs) {
454- if (!is_contained (NewSafeToDerefRegs, TrustedReg))
455- NewSafeToDerefRegs.push_back (TrustedReg);
456- }
476+ // If authentication instructions trap on failure, safe-to-dereference
477+ // registers are always trusted.
478+ SmallVector<MCPhysReg> NewTrustedRegs =
479+ AuthTrapsOnFailure ? NewSafeToDerefRegs
480+ : getRegsMadeTrusted (Point, Cur);
457481
458482 // Then, compute the state after this instruction is executed.
459483 SrcState Next = Cur;
@@ -490,6 +514,11 @@ class SrcSafetyAnalysis {
490514 dbgs () << " )\n " ;
491515 });
492516
517+ // Being trusted is a strictly stronger property than being
518+ // safe-to-dereference.
519+ assert (!Next.TrustedRegs .test (Next.SafeToDerefRegs ) &&
520+ " SafeToDerefRegs should contain all TrustedRegs" );
521+
493522 return Next;
494523 }
495524
@@ -1066,6 +1095,11 @@ class DataflowDstSafetyAnalysis
10661095 }
10671096
10681097 void run () override {
1098+ // As long as DstSafetyAnalysis is only computed to detect authentication
1099+ // oracles, it is a waste of time to compute it when authentication
1100+ // instructions are known to always trap on failure.
1101+ assert (!AuthTrapsOnFailure &&
1102+ " DstSafetyAnalysis is useless with faulting auth" );
10691103 for (BinaryBasicBlock &BB : Func) {
10701104 if (auto CheckerInfo = BC.MIB ->getAuthCheckedReg (BB)) {
10711105 LLVM_DEBUG ({
@@ -1536,6 +1570,8 @@ void FunctionAnalysisContext::findUnsafeDefs(
15361570 SmallVector<PartialReport<MCPhysReg>> &Reports) {
15371571 if (PacRetGadgetsOnly)
15381572 return ;
1573+ if (AuthTrapsOnFailure)
1574+ return ;
15391575
15401576 auto Analysis = DstSafetyAnalysis::create (BF, AllocatorId, {});
15411577 LLVM_DEBUG ({ dbgs () << " Running dst register safety analysis...\n " ; });
0 commit comments