@@ -194,10 +194,24 @@ template <typename T> static void iterateOverInstrs(BinaryFunction &BF, T Fn) {
194194// / X30 is safe-to-dereference - the state computed for sub- and
195195// / super-registers is not inspected.
196196struct 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.
200205 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;
201215 // / A vector of sets, only used in the second data flow run.
202216 // / Each element in the vector represents one of the registers for which we
203217 // / track the set of last instructions that wrote to this register. For
@@ -210,7 +224,8 @@ struct State {
210224 State () {}
211225
212226 State (unsigned NumRegs, unsigned NumRegsToTrack)
213- : SafeToDerefRegs(NumRegs), LastInstWritingReg(NumRegsToTrack) {}
227+ : SafeToDerefRegs(NumRegs), TrustedRegs(NumRegs),
228+ LastInstWritingReg (NumRegsToTrack) {}
214229
215230 State &merge (const State &StateIn) {
216231 if (StateIn.empty ())
@@ -219,6 +234,7 @@ struct State {
219234 return (*this = StateIn);
220235
221236 SafeToDerefRegs &= StateIn.SafeToDerefRegs ;
237+ TrustedRegs &= StateIn.TrustedRegs ;
222238 for (unsigned I = 0 ; I < LastInstWritingReg.size (); ++I)
223239 for (const MCInst *J : StateIn.LastInstWritingReg [I])
224240 LastInstWritingReg[I].insert (J);
@@ -231,6 +247,7 @@ struct State {
231247
232248 bool operator ==(const State &RHS) const {
233249 return SafeToDerefRegs == RHS.SafeToDerefRegs &&
250+ TrustedRegs == RHS.TrustedRegs &&
234251 LastInstWritingReg == RHS.LastInstWritingReg ;
235252 }
236253 bool operator !=(const State &RHS) const { return !((*this ) == RHS); }
@@ -255,6 +272,7 @@ raw_ostream &operator<<(raw_ostream &OS, const State &S) {
255272 OS << " empty" ;
256273 } else {
257274 OS << " SafeToDerefRegs: " << S.SafeToDerefRegs << " , " ;
275+ OS << " TrustedRegs: " << S.TrustedRegs << " , " ;
258276 printLastInsts (OS, S.LastInstWritingReg );
259277 }
260278 OS << " >" ;
@@ -275,11 +293,14 @@ void PacStatePrinter::print(raw_ostream &OS, const State &S) const {
275293 OS << " pacret-state<" ;
276294 if (S.empty ()) {
277295 assert (S.SafeToDerefRegs .empty ());
296+ assert (S.TrustedRegs .empty ());
278297 assert (S.LastInstWritingReg .empty ());
279298 OS << " empty" ;
280299 } else {
281300 OS << " SafeToDerefRegs: " ;
282301 RegStatePrinter.print (OS, S.SafeToDerefRegs );
302+ OS << " , TrustedRegs: " ;
303+ RegStatePrinter.print (OS, S.TrustedRegs );
283304 OS << " , " ;
284305 printLastInsts (OS, S.LastInstWritingReg );
285306 }
@@ -308,6 +329,17 @@ class PacRetAnalysis {
308329 // / RegToTrackInstsFor is the set of registers for which the dataflow analysis
309330 // / must compute which the last set of instructions writing to it are.
310331 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;
311343
312344 SmallPtrSet<const MCInst *, 4 > &lastWritingInsts (State &S,
313345 MCPhysReg Reg) const {
@@ -322,8 +354,10 @@ class PacRetAnalysis {
322354
323355 State createEntryState () {
324356 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+ }
327361 return S;
328362 }
329363
@@ -370,6 +404,45 @@ class PacRetAnalysis {
370404 return Regs;
371405 }
372406
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+
373446 State computeNext (const MCInst &Point, const State &Cur) {
374447 PacStatePrinter P (BC);
375448 LLVM_DEBUG ({
@@ -396,11 +469,34 @@ class PacRetAnalysis {
396469 BitVector Clobbered = getClobberedRegs (Point);
397470 SmallVector<MCPhysReg> NewSafeToDerefRegs =
398471 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+ }
399494
400495 // Then, compute the state after this instruction is executed.
401496 State Next = Cur;
402497
403498 Next.SafeToDerefRegs .reset (Clobbered);
499+ Next.TrustedRegs .reset (Clobbered);
404500 // Keep track of this instruction if it writes to any of the registers we
405501 // need to track that for:
406502 for (MCPhysReg Reg : RegsToTrackInstsFor.getRegisters ())
@@ -421,6 +517,10 @@ class PacRetAnalysis {
421517 lastWritingInsts (Next, Reg).clear ();
422518 }
423519
520+ // Process new trusted registers.
521+ for (MCPhysReg TrustedReg : NewTrustedRegs)
522+ Next.TrustedRegs |= BC.MIB ->getAliases (TrustedReg, /* OnlySmaller=*/ true );
523+
424524 LLVM_DEBUG ({
425525 dbgs () << " .. result: (" ;
426526 P.print (dbgs (), Next);
@@ -476,7 +576,22 @@ class PacRetDFAnalysis
476576 return DFParent::getStateBefore (Inst);
477577 }
478578
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+ }
480595
481596protected:
482597 void preflight () {}
@@ -634,6 +749,26 @@ shouldReportCallGadget(const BinaryContext &BC, const MCInstReference &Inst,
634749 return std::make_shared<GadgetReport>(CallKind, Inst, DestReg);
635750}
636751
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+
637772FunctionAnalysisResult
638773Analysis::findGadgets (BinaryFunction &BF,
639774 MCPlusBuilder::AllocatorIdTy AllocatorId) {
@@ -666,6 +801,8 @@ Analysis::findGadgets(BinaryFunction &BF,
666801
667802 if (auto Report = shouldReportCallGadget (BC, Inst, S))
668803 Result.Diagnostics .push_back (Report);
804+ if (auto Report = shouldReportSigningOracle (BC, Inst, S))
805+ Result.Diagnostics .push_back (Report);
669806 });
670807 return Result;
671808}
0 commit comments