@@ -194,10 +194,24 @@ template <typename T> static void iterateOverInstrs(BinaryFunction &BF, T Fn) {
194
194
// / X30 is safe-to-dereference - the state computed for sub- and
195
195
// / super-registers is not inspected.
196
196
struct State {
197
- // / A BitVector containing the registers that are either safe at function
198
- // / entry and were not clobbered yet, or those not clobbered since being
199
- // / authenticated.
197
+ // / A BitVector containing the registers that are either authenticated
198
+ // / (assuming failed authentication is permitted to produce an invalid
199
+ // / address, provided it generates an error on memory access) or whose
200
+ // / value is known not to be attacker-controlled under Pointer Authentication
201
+ // / threat model. The registers in this set are either
202
+ // / * not clobbered since being authenticated, or
203
+ // / * trusted at function entry and were not clobbered yet, or
204
+ // / * contain a safely materialized address.
200
205
BitVector SafeToDerefRegs;
206
+ // / A BitVector containing the registers that are either authenticated
207
+ // / *successfully* or whose value is known not to be attacker-controlled
208
+ // / under Pointer Authentication threat model.
209
+ // / The registers in this set are either
210
+ // / * authenticated and then checked to be authenticated successfully
211
+ // / (and not clobbered since then), or
212
+ // / * trusted at function entry and were not clobbered yet, or
213
+ // / * contain a safely materialized address.
214
+ BitVector TrustedRegs;
201
215
// / A vector of sets, only used in the second data flow run.
202
216
// / Each element in the vector represents one of the registers for which we
203
217
// / track the set of last instructions that wrote to this register. For
@@ -210,7 +224,8 @@ struct State {
210
224
State () {}
211
225
212
226
State (unsigned NumRegs, unsigned NumRegsToTrack)
213
- : SafeToDerefRegs(NumRegs), LastInstWritingReg(NumRegsToTrack) {}
227
+ : SafeToDerefRegs(NumRegs), TrustedRegs(NumRegs),
228
+ LastInstWritingReg (NumRegsToTrack) {}
214
229
215
230
State &merge (const State &StateIn) {
216
231
if (StateIn.empty ())
@@ -219,6 +234,7 @@ struct State {
219
234
return (*this = StateIn);
220
235
221
236
SafeToDerefRegs &= StateIn.SafeToDerefRegs ;
237
+ TrustedRegs &= StateIn.TrustedRegs ;
222
238
for (unsigned I = 0 ; I < LastInstWritingReg.size (); ++I)
223
239
for (const MCInst *J : StateIn.LastInstWritingReg [I])
224
240
LastInstWritingReg[I].insert (J);
@@ -231,6 +247,7 @@ struct State {
231
247
232
248
bool operator ==(const State &RHS) const {
233
249
return SafeToDerefRegs == RHS.SafeToDerefRegs &&
250
+ TrustedRegs == RHS.TrustedRegs &&
234
251
LastInstWritingReg == RHS.LastInstWritingReg ;
235
252
}
236
253
bool operator !=(const State &RHS) const { return !((*this ) == RHS); }
@@ -255,6 +272,7 @@ raw_ostream &operator<<(raw_ostream &OS, const State &S) {
255
272
OS << " empty" ;
256
273
} else {
257
274
OS << " SafeToDerefRegs: " << S.SafeToDerefRegs << " , " ;
275
+ OS << " TrustedRegs: " << S.TrustedRegs << " , " ;
258
276
printLastInsts (OS, S.LastInstWritingReg );
259
277
}
260
278
OS << " >" ;
@@ -275,11 +293,14 @@ void PacStatePrinter::print(raw_ostream &OS, const State &S) const {
275
293
OS << " pacret-state<" ;
276
294
if (S.empty ()) {
277
295
assert (S.SafeToDerefRegs .empty ());
296
+ assert (S.TrustedRegs .empty ());
278
297
assert (S.LastInstWritingReg .empty ());
279
298
OS << " empty" ;
280
299
} else {
281
300
OS << " SafeToDerefRegs: " ;
282
301
RegStatePrinter.print (OS, S.SafeToDerefRegs );
302
+ OS << " , TrustedRegs: " ;
303
+ RegStatePrinter.print (OS, S.TrustedRegs );
283
304
OS << " , " ;
284
305
printLastInsts (OS, S.LastInstWritingReg );
285
306
}
@@ -308,6 +329,17 @@ class PacRetAnalysis {
308
329
// / RegToTrackInstsFor is the set of registers for which the dataflow analysis
309
330
// / must compute which the last set of instructions writing to it are.
310
331
const TrackedRegisters RegsToTrackInstsFor;
332
+ // / Stores information about the detected instruction sequences emitted to
333
+ // / check an authenticated pointer. Specifically, if such sequence is detected
334
+ // / in a basic block, it maps the last instruction of that basic block to
335
+ // / (CheckedRegister, FirstInstOfTheSequence) pair, see the description of
336
+ // / MCPlusBuilder::getAuthCheckedReg(BB) method.
337
+ // /
338
+ // / As the detection of such sequences requires iterating over the adjacent
339
+ // / instructions, it should be done before calling computeNext(), which
340
+ // / operates on separate instructions.
341
+ DenseMap<const MCInst *, std::pair<MCPhysReg, const MCInst *>>
342
+ CheckerSequenceInfo;
311
343
312
344
SmallPtrSet<const MCInst *, 4 > &lastWritingInsts (State &S,
313
345
MCPhysReg Reg) const {
@@ -322,8 +354,10 @@ class PacRetAnalysis {
322
354
323
355
State createEntryState () {
324
356
State S (NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters ());
325
- for (MCPhysReg Reg : BC.MIB ->getTrustedLiveInRegs ())
326
- S.SafeToDerefRegs |= BC.MIB ->getAliases (Reg, /* OnlySmaller=*/ true );
357
+ for (MCPhysReg Reg : BC.MIB ->getTrustedLiveInRegs ()) {
358
+ S.TrustedRegs |= BC.MIB ->getAliases (Reg, /* OnlySmaller=*/ true );
359
+ S.SafeToDerefRegs = S.TrustedRegs ;
360
+ }
327
361
return S;
328
362
}
329
363
@@ -370,6 +404,45 @@ class PacRetAnalysis {
370
404
return Regs;
371
405
}
372
406
407
+ // Returns all registers made trusted by this instruction.
408
+ SmallVector<MCPhysReg> getRegsMadeTrusted (const MCInst &Point,
409
+ const State &Cur) const {
410
+ SmallVector<MCPhysReg> Regs;
411
+ const MCPhysReg NoReg = BC.MIB ->getNoRegister ();
412
+
413
+ // An authenticated pointer can be checked, or
414
+ MCPhysReg CheckedReg =
415
+ BC.MIB ->getAuthCheckedReg (Point, /* MayOverwrite=*/ false );
416
+ if (CheckedReg != NoReg && Cur.SafeToDerefRegs [CheckedReg])
417
+ Regs.push_back (CheckedReg);
418
+
419
+ if (CheckerSequenceInfo.contains (&Point)) {
420
+ MCPhysReg CheckedReg;
421
+ const MCInst *FirstCheckerInst;
422
+ std::tie (CheckedReg, FirstCheckerInst) = CheckerSequenceInfo.at (&Point);
423
+
424
+ // FirstCheckerInst should belong to the same basic block, meaning
425
+ // it was deterministically processed a few steps before this instruction.
426
+ const State &StateBeforeChecker = getStateBefore (*FirstCheckerInst).get ();
427
+ if (StateBeforeChecker.SafeToDerefRegs [CheckedReg])
428
+ Regs.push_back (CheckedReg);
429
+ }
430
+
431
+ // ... a safe address can be materialized, or
432
+ MCPhysReg NewAddrReg = BC.MIB ->getMaterializedAddressRegForPtrAuth (Point);
433
+ if (NewAddrReg != NoReg)
434
+ Regs.push_back (NewAddrReg);
435
+
436
+ // ... an address can be updated in a safe manner, producing the result
437
+ // which is as trusted as the input address.
438
+ if (auto DstAndSrc = BC.MIB ->analyzeAddressArithmeticsForPtrAuth (Point)) {
439
+ if (Cur.TrustedRegs [DstAndSrc->second ])
440
+ Regs.push_back (DstAndSrc->first );
441
+ }
442
+
443
+ return Regs;
444
+ }
445
+
373
446
State computeNext (const MCInst &Point, const State &Cur) {
374
447
PacStatePrinter P (BC);
375
448
LLVM_DEBUG ({
@@ -396,11 +469,34 @@ class PacRetAnalysis {
396
469
BitVector Clobbered = getClobberedRegs (Point);
397
470
SmallVector<MCPhysReg> NewSafeToDerefRegs =
398
471
getRegsMadeSafeToDeref (Point, Cur);
472
+ SmallVector<MCPhysReg> NewTrustedRegs = getRegsMadeTrusted (Point, Cur);
473
+
474
+ // Ideally, being trusted is a strictly stronger property than being
475
+ // safe-to-dereference. To simplify the computation of Next state, enforce
476
+ // this for NewSafeToDerefRegs and NewTrustedRegs. Additionally, this
477
+ // fixes the properly for "cumulative" register states in tricky cases
478
+ // like the following:
479
+ //
480
+ // ; LR is safe to dereference here
481
+ // mov x16, x30 ; start of the sequence, LR is s-t-d right before
482
+ // xpaclri ; clobbers LR, LR is not safe anymore
483
+ // cmp x30, x16
484
+ // b.eq 1f ; end of the sequence: LR is marked as trusted
485
+ // brk 0x1234
486
+ // 1:
487
+ // ; at this point LR would be marked as trusted,
488
+ // ; but not safe-to-dereference
489
+ //
490
+ for (auto TrustedReg : NewTrustedRegs) {
491
+ if (!is_contained (NewSafeToDerefRegs, TrustedReg))
492
+ NewSafeToDerefRegs.push_back (TrustedReg);
493
+ }
399
494
400
495
// Then, compute the state after this instruction is executed.
401
496
State Next = Cur;
402
497
403
498
Next.SafeToDerefRegs .reset (Clobbered);
499
+ Next.TrustedRegs .reset (Clobbered);
404
500
// Keep track of this instruction if it writes to any of the registers we
405
501
// need to track that for:
406
502
for (MCPhysReg Reg : RegsToTrackInstsFor.getRegisters ())
@@ -421,6 +517,10 @@ class PacRetAnalysis {
421
517
lastWritingInsts (Next, Reg).clear ();
422
518
}
423
519
520
+ // Process new trusted registers.
521
+ for (MCPhysReg TrustedReg : NewTrustedRegs)
522
+ Next.TrustedRegs |= BC.MIB ->getAliases (TrustedReg, /* OnlySmaller=*/ true );
523
+
424
524
LLVM_DEBUG ({
425
525
dbgs () << " .. result: (" ;
426
526
P.print (dbgs (), Next);
@@ -476,7 +576,22 @@ class PacRetDFAnalysis
476
576
return DFParent::getStateBefore (Inst);
477
577
}
478
578
479
- void run () override { DFParent::run (); }
579
+ void run () override {
580
+ for (BinaryBasicBlock &BB : Func) {
581
+ if (auto CheckerInfo = BC.MIB ->getAuthCheckedReg (BB)) {
582
+ MCInst *LastInstOfChecker = BB.getLastNonPseudoInstr ();
583
+ LLVM_DEBUG ({
584
+ dbgs () << " Found pointer checking sequence in " << BB.getName ()
585
+ << " :\n " ;
586
+ traceReg (BC, " Checked register" , CheckerInfo->first );
587
+ traceInst (BC, " First instruction" , *CheckerInfo->second );
588
+ traceInst (BC, " Last instruction" , *LastInstOfChecker);
589
+ });
590
+ CheckerSequenceInfo[LastInstOfChecker] = *CheckerInfo;
591
+ }
592
+ }
593
+ DFParent::run ();
594
+ }
480
595
481
596
protected:
482
597
void preflight () {}
@@ -634,6 +749,26 @@ shouldReportCallGadget(const BinaryContext &BC, const MCInstReference &Inst,
634
749
return std::make_shared<GadgetReport>(CallKind, Inst, DestReg);
635
750
}
636
751
752
+ static std::shared_ptr<Report>
753
+ shouldReportSigningOracle (const BinaryContext &BC, const MCInstReference &Inst,
754
+ const State &S) {
755
+ static const GadgetKind SigningOracleKind (" signing oracle found" );
756
+
757
+ MCPhysReg SignedReg = BC.MIB ->getSignedReg (Inst);
758
+ if (SignedReg == BC.MIB ->getNoRegister ())
759
+ return nullptr ;
760
+
761
+ LLVM_DEBUG ({
762
+ traceInst (BC, " Found sign inst" , Inst);
763
+ traceReg (BC, " Signed reg" , SignedReg);
764
+ traceRegMask (BC, " TrustedRegs" , S.TrustedRegs );
765
+ });
766
+ if (S.TrustedRegs [SignedReg])
767
+ return nullptr ;
768
+
769
+ return std::make_shared<GadgetReport>(SigningOracleKind, Inst, SignedReg);
770
+ }
771
+
637
772
FunctionAnalysisResult
638
773
Analysis::findGadgets (BinaryFunction &BF,
639
774
MCPlusBuilder::AllocatorIdTy AllocatorId) {
@@ -666,6 +801,8 @@ Analysis::findGadgets(BinaryFunction &BF,
666
801
667
802
if (auto Report = shouldReportCallGadget (BC, Inst, S))
668
803
Result.Diagnostics .push_back (Report);
804
+ if (auto Report = shouldReportSigningOracle (BC, Inst, S))
805
+ Result.Diagnostics .push_back (Report);
669
806
});
670
807
return Result;
671
808
}
0 commit comments