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 << " : " ;
@@ -332,6 +338,34 @@ class SrcSafetyAnalysis {
332
338
return Clobbered;
333
339
}
334
340
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
+
335
369
// Returns all registers that can be treated as if they are written by an
336
370
// authentication instruction.
337
371
SmallVector<MCPhysReg> getRegsMadeSafeToDeref (const MCInst &Point ,
@@ -354,18 +388,38 @@ class SrcSafetyAnalysis {
354
388
Regs.push_back (DstAndSrc->first );
355
389
}
356
390
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
+
357
412
return Regs;
358
413
}
359
414
360
415
// Returns all registers made trusted by this instruction.
361
416
SmallVector<MCPhysReg> getRegsMadeTrusted (const MCInst &Point ,
362
417
const SrcState &Cur) const {
418
+ assert (!AuthTrapsOnFailure && " Use getRegsMadeSafeToDeref instead" );
363
419
SmallVector<MCPhysReg> Regs;
364
420
365
421
// 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))
369
423
Regs.push_back (*CheckedReg);
370
424
371
425
// ... a pointer can be authenticated by an instruction that always checks
@@ -376,19 +430,6 @@ class SrcSafetyAnalysis {
376
430
if (AutReg && IsChecked)
377
431
Regs.push_back (*AutReg);
378
432
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
-
392
433
// ... a safe address can be materialized, or
393
434
if (auto NewAddrReg = BC.MIB ->getMaterializedAddressRegForPtrAuth (Point ))
394
435
Regs.push_back (*NewAddrReg);
@@ -432,28 +473,11 @@ class SrcSafetyAnalysis {
432
473
BitVector Clobbered = getClobberedRegs (Point );
433
474
SmallVector<MCPhysReg> NewSafeToDerefRegs =
434
475
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);
457
481
458
482
// Then, compute the state after this instruction is executed.
459
483
SrcState Next = Cur;
@@ -490,6 +514,11 @@ class SrcSafetyAnalysis {
490
514
dbgs () << " )\n " ;
491
515
});
492
516
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
+
493
522
return Next;
494
523
}
495
524
@@ -1066,6 +1095,11 @@ class DataflowDstSafetyAnalysis
1066
1095
}
1067
1096
1068
1097
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" );
1069
1103
for (BinaryBasicBlock &BB : Func) {
1070
1104
if (auto CheckerInfo = BC.MIB ->getAuthCheckedReg (BB)) {
1071
1105
LLVM_DEBUG ({
@@ -1536,6 +1570,8 @@ void FunctionAnalysisContext::findUnsafeDefs(
1536
1570
SmallVector<PartialReport<MCPhysReg>> &Reports) {
1537
1571
if (PacRetGadgetsOnly)
1538
1572
return ;
1573
+ if (AuthTrapsOnFailure)
1574
+ return ;
1539
1575
1540
1576
auto Analysis = DstSafetyAnalysis::create (BF, AllocatorId, {});
1541
1577
LLVM_DEBUG ({ dbgs () << " Running dst register safety analysis...\n " ; });
0 commit comments