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 << " : " ;
@@ -364,6 +370,34 @@ class SrcSafetyAnalysis {
364
370
return Clobbered;
365
371
}
366
372
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
+
367
401
// Returns all registers that can be treated as if they are written by an
368
402
// authentication instruction.
369
403
SmallVector<MCPhysReg> getRegsMadeSafeToDeref (const MCInst &Point ,
@@ -386,18 +420,38 @@ class SrcSafetyAnalysis {
386
420
Regs.push_back (DstAndSrc->first );
387
421
}
388
422
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
+
389
444
return Regs;
390
445
}
391
446
392
447
// Returns all registers made trusted by this instruction.
393
448
SmallVector<MCPhysReg> getRegsMadeTrusted (const MCInst &Point ,
394
449
const SrcState &Cur) const {
450
+ assert (!AuthTrapsOnFailure && " Use getRegsMadeSafeToDeref instead" );
395
451
SmallVector<MCPhysReg> Regs;
396
452
397
453
// 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))
401
455
Regs.push_back (*CheckedReg);
402
456
403
457
// ... a pointer can be authenticated by an instruction that always checks
@@ -408,19 +462,6 @@ class SrcSafetyAnalysis {
408
462
if (AutReg && IsChecked)
409
463
Regs.push_back (*AutReg);
410
464
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
-
424
465
// ... a safe address can be materialized, or
425
466
if (auto NewAddrReg = BC.MIB ->getMaterializedAddressRegForPtrAuth (Point ))
426
467
Regs.push_back (*NewAddrReg);
@@ -463,28 +504,11 @@ class SrcSafetyAnalysis {
463
504
BitVector Clobbered = getClobberedRegs (Point );
464
505
SmallVector<MCPhysReg> NewSafeToDerefRegs =
465
506
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);
488
512
489
513
// Then, compute the state after this instruction is executed.
490
514
SrcState Next = Cur;
@@ -521,6 +545,11 @@ class SrcSafetyAnalysis {
521
545
dbgs () << " )\n " ;
522
546
});
523
547
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
+
524
553
return Next;
525
554
}
526
555
@@ -1124,6 +1153,11 @@ class DataflowDstSafetyAnalysis
1124
1153
}
1125
1154
1126
1155
void run () override {
1156
+ // As long as DstSafetyAnalysis is only computed to detect authentication
1157
+ // oracles, it is a waste of time to compute it when authentication
1158
+ // instructions are known to always trap on failure.
1159
+ assert (!AuthTrapsOnFailure &&
1160
+ " DstSafetyAnalysis is useless with faulting auth" );
1127
1161
for (BinaryBasicBlock &BB : Func) {
1128
1162
if (auto CheckerInfo = BC.MIB ->getAuthCheckedReg (BB)) {
1129
1163
LLVM_DEBUG ({
@@ -1564,6 +1598,8 @@ void FunctionAnalysisContext::findUnsafeDefs(
1564
1598
SmallVector<PartialReport<MCPhysReg>> &Reports) {
1565
1599
if (PacRetGadgetsOnly)
1566
1600
return ;
1601
+ if (AuthTrapsOnFailure)
1602
+ return ;
1567
1603
1568
1604
auto Analysis = DstSafetyAnalysis::create (BF, AllocatorId, {});
1569
1605
LLVM_DEBUG ({ dbgs () << " Running dst register safety analysis...\n " ; });
0 commit comments