14
14
#include " bolt/Passes/PAuthGadgetScanner.h"
15
15
#include " bolt/Core/ParallelUtilities.h"
16
16
#include " bolt/Passes/DataflowAnalysis.h"
17
+ #include " bolt/Utils/CommandLineOpts.h"
17
18
#include " llvm/ADT/STLExtras.h"
18
19
#include " llvm/ADT/SmallSet.h"
19
20
#include " llvm/MC/MCInst.h"
@@ -26,6 +27,11 @@ namespace llvm {
26
27
namespace bolt {
27
28
namespace PAuthGadgetScanner {
28
29
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
+
29
35
[[maybe_unused]] static void traceInst (const BinaryContext &BC, StringRef Label,
30
36
const MCInst &MI) {
31
37
dbgs () << " " << Label << " : " ;
@@ -365,6 +371,34 @@ class SrcSafetyAnalysis {
365
371
return Clobbered;
366
372
}
367
373
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
+
368
402
// Returns all registers that can be treated as if they are written by an
369
403
// authentication instruction.
370
404
SmallVector<MCPhysReg> getRegsMadeSafeToDeref (const MCInst &Point,
@@ -387,18 +421,38 @@ class SrcSafetyAnalysis {
387
421
Regs.push_back (DstAndSrc->first );
388
422
}
389
423
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
+
390
445
return Regs;
391
446
}
392
447
393
448
// Returns all registers made trusted by this instruction.
394
449
SmallVector<MCPhysReg> getRegsMadeTrusted (const MCInst &Point,
395
450
const SrcState &Cur) const {
451
+ assert (!AuthTrapsOnFailure && " Use getRegsMadeSafeToDeref instead" );
396
452
SmallVector<MCPhysReg> Regs;
397
453
398
454
// 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))
402
456
Regs.push_back (*CheckedReg);
403
457
404
458
// ... a pointer can be authenticated by an instruction that always checks
@@ -409,19 +463,6 @@ class SrcSafetyAnalysis {
409
463
if (AutReg && IsChecked)
410
464
Regs.push_back (*AutReg);
411
465
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
-
425
466
// ... a safe address can be materialized, or
426
467
if (auto NewAddrReg = BC.MIB ->getMaterializedAddressRegForPtrAuth (Point))
427
468
Regs.push_back (*NewAddrReg);
@@ -465,28 +506,11 @@ class SrcSafetyAnalysis {
465
506
BitVector Clobbered = getClobberedRegs (Point);
466
507
SmallVector<MCPhysReg> NewSafeToDerefRegs =
467
508
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);
490
514
491
515
// Then, compute the state after this instruction is executed.
492
516
SrcState Next = Cur;
@@ -523,6 +547,11 @@ class SrcSafetyAnalysis {
523
547
dbgs () << " )\n " ;
524
548
});
525
549
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
+
526
555
return Next;
527
556
}
528
557
@@ -1084,6 +1113,11 @@ class DataflowDstSafetyAnalysis
1084
1113
}
1085
1114
1086
1115
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" );
1087
1121
for (BinaryBasicBlock &BB : Func) {
1088
1122
if (auto CheckerInfo = BC.MIB ->getAuthCheckedReg (BB)) {
1089
1123
LLVM_DEBUG ({
@@ -1543,6 +1577,8 @@ void FunctionAnalysisContext::findUnsafeDefs(
1543
1577
SmallVector<PartialReport<MCPhysReg>> &Reports) {
1544
1578
if (PacRetGadgetsOnly)
1545
1579
return ;
1580
+ if (AuthTrapsOnFailure)
1581
+ return ;
1546
1582
1547
1583
auto Analysis = DstSafetyAnalysis::create (BF, AllocatorId, {});
1548
1584
LLVM_DEBUG ({ dbgs () << " Running dst register safety analysis...\n " ; });
0 commit comments