@@ -173,10 +173,24 @@ class TrackedRegisters {
173173// / X30 is safe-to-dereference - the state computed for sub- and
174174// / super-registers is not inspected.
175175struct SrcState {
176- // / A BitVector containing the registers that are either safe at function
177- // / entry and were not clobbered yet, or those not clobbered since being
178- // / authenticated.
176+ // / A BitVector containing the registers that are either authenticated
177+ // / (assuming failed authentication is permitted to produce an invalid
178+ // / address, provided it generates an error on memory access) or whose
179+ // / value is known not to be attacker-controlled under Pointer Authentication
180+ // / threat model. The registers in this set are either
181+ // / * not clobbered since being authenticated, or
182+ // / * trusted at function entry and were not clobbered yet, or
183+ // / * contain a safely materialized address.
179184 BitVector SafeToDerefRegs;
185+ // / A BitVector containing the registers that are either authenticated
186+ // / *successfully* or whose value is known not to be attacker-controlled
187+ // / under Pointer Authentication threat model.
188+ // / The registers in this set are either
189+ // / * authenticated and then checked to be authenticated successfully
190+ // / (and not clobbered since then), or
191+ // / * trusted at function entry and were not clobbered yet, or
192+ // / * contain a safely materialized address.
193+ BitVector TrustedRegs;
180194 // / A vector of sets, only used in the second data flow run.
181195 // / Each element in the vector represents one of the registers for which we
182196 // / track the set of last instructions that wrote to this register. For
@@ -189,7 +203,8 @@ struct SrcState {
189203 SrcState () {}
190204
191205 SrcState (unsigned NumRegs, unsigned NumRegsToTrack)
192- : SafeToDerefRegs(NumRegs), LastInstWritingReg(NumRegsToTrack) {}
206+ : SafeToDerefRegs(NumRegs), TrustedRegs(NumRegs),
207+ LastInstWritingReg (NumRegsToTrack) {}
193208
194209 SrcState &merge (const SrcState &StateIn) {
195210 if (StateIn.empty ())
@@ -198,6 +213,7 @@ struct SrcState {
198213 return (*this = StateIn);
199214
200215 SafeToDerefRegs &= StateIn.SafeToDerefRegs ;
216+ TrustedRegs &= StateIn.TrustedRegs ;
201217 for (unsigned I = 0 ; I < LastInstWritingReg.size (); ++I)
202218 for (const MCInst *J : StateIn.LastInstWritingReg [I])
203219 LastInstWritingReg[I].insert (J);
@@ -210,6 +226,7 @@ struct SrcState {
210226
211227 bool operator ==(const SrcState &RHS) const {
212228 return SafeToDerefRegs == RHS.SafeToDerefRegs &&
229+ TrustedRegs == RHS.TrustedRegs &&
213230 LastInstWritingReg == RHS.LastInstWritingReg ;
214231 }
215232 bool operator !=(const SrcState &RHS) const { return !((*this ) == RHS); }
@@ -234,6 +251,7 @@ raw_ostream &operator<<(raw_ostream &OS, const SrcState &S) {
234251 OS << " empty" ;
235252 } else {
236253 OS << " SafeToDerefRegs: " << S.SafeToDerefRegs << " , " ;
254+ OS << " TrustedRegs: " << S.TrustedRegs << " , " ;
237255 printLastInsts (OS, S.LastInstWritingReg );
238256 }
239257 OS << " >" ;
@@ -254,18 +272,22 @@ void SrcStatePrinter::print(raw_ostream &OS, const SrcState &S) const {
254272 OS << " src-state<" ;
255273 if (S.empty ()) {
256274 assert (S.SafeToDerefRegs .empty ());
275+ assert (S.TrustedRegs .empty ());
257276 assert (S.LastInstWritingReg .empty ());
258277 OS << " empty" ;
259278 } else {
260279 OS << " SafeToDerefRegs: " ;
261280 RegStatePrinter.print (OS, S.SafeToDerefRegs );
281+ OS << " , TrustedRegs: " ;
282+ RegStatePrinter.print (OS, S.TrustedRegs );
262283 OS << " , " ;
263284 printLastInsts (OS, S.LastInstWritingReg );
264285 }
265286 OS << " >" ;
266287}
267288
268- // / Computes which registers are safe to be used by control flow instructions.
289+ // / Computes which registers are safe to be used by control flow and signing
290+ // / instructions.
269291// /
270292// / This is the base class for two implementations: a dataflow-based analysis
271293// / which is intended to be used for most functions and a simplified CFG-unaware
@@ -293,6 +315,17 @@ class SrcSafetyAnalysis {
293315 // / RegToTrackInstsFor is the set of registers for which the dataflow analysis
294316 // / must compute which the last set of instructions writing to it are.
295317 const TrackedRegisters RegsToTrackInstsFor;
318+ // / Stores information about the detected instruction sequences emitted to
319+ // / check an authenticated pointer. Specifically, if such sequence is detected
320+ // / in a basic block, it maps the last instruction of that basic block to
321+ // / (CheckedRegister, FirstInstOfTheSequence) pair, see the description of
322+ // / MCPlusBuilder::getAuthCheckedReg(BB) method.
323+ // /
324+ // / As the detection of such sequences requires iterating over the adjacent
325+ // / instructions, it should be done before calling computeNext(), which
326+ // / operates on separate instructions.
327+ DenseMap<const MCInst *, std::pair<MCPhysReg, const MCInst *>>
328+ CheckerSequenceInfo;
296329
297330 SmallPtrSet<const MCInst *, 4 > &lastWritingInsts (SrcState &S,
298331 MCPhysReg Reg) const {
@@ -308,7 +341,8 @@ class SrcSafetyAnalysis {
308341 SrcState createEntryState () {
309342 SrcState S (NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters ());
310343 for (MCPhysReg Reg : BC.MIB ->getTrustedLiveInRegs ())
311- S.SafeToDerefRegs |= BC.MIB ->getAliases (Reg, /* OnlySmaller=*/ true );
344+ S.TrustedRegs |= BC.MIB ->getAliases (Reg, /* OnlySmaller=*/ true );
345+ S.SafeToDerefRegs = S.TrustedRegs ;
312346 return S;
313347 }
314348
@@ -355,6 +389,47 @@ class SrcSafetyAnalysis {
355389 return Regs;
356390 }
357391
392+ // Returns all registers made trusted by this instruction.
393+ SmallVector<MCPhysReg> getRegsMadeTrusted (const MCInst &Point,
394+ const SrcState &Cur) const {
395+ SmallVector<MCPhysReg> Regs;
396+ const MCPhysReg NoReg = BC.MIB ->getNoRegister ();
397+
398+ // An authenticated pointer can be checked, or
399+ MCPhysReg CheckedReg =
400+ BC.MIB ->getAuthCheckedReg (Point, /* MayOverwrite=*/ false );
401+ if (CheckedReg != NoReg && Cur.SafeToDerefRegs [CheckedReg])
402+ Regs.push_back (CheckedReg);
403+
404+ if (CheckerSequenceInfo.contains (&Point)) {
405+ MCPhysReg CheckedReg;
406+ const MCInst *FirstCheckerInst;
407+ std::tie (CheckedReg, FirstCheckerInst) = CheckerSequenceInfo.at (&Point);
408+
409+ // FirstCheckerInst should belong to the same basic block (see the
410+ // assertion in DataflowSrcSafetyAnalysis::run()), meaning it was
411+ // deterministically processed a few steps before this instruction.
412+ const SrcState &StateBeforeChecker =
413+ getStateBefore (*FirstCheckerInst).get ();
414+ if (StateBeforeChecker.SafeToDerefRegs [CheckedReg])
415+ Regs.push_back (CheckedReg);
416+ }
417+
418+ // ... a safe address can be materialized, or
419+ MCPhysReg NewAddrReg = BC.MIB ->getMaterializedAddressRegForPtrAuth (Point);
420+ if (NewAddrReg != NoReg)
421+ Regs.push_back (NewAddrReg);
422+
423+ // ... an address can be updated in a safe manner, producing the result
424+ // which is as trusted as the input address.
425+ if (auto DstAndSrc = BC.MIB ->analyzeAddressArithmeticsForPtrAuth (Point)) {
426+ if (Cur.TrustedRegs [DstAndSrc->second ])
427+ Regs.push_back (DstAndSrc->first );
428+ }
429+
430+ return Regs;
431+ }
432+
358433 SrcState computeNext (const MCInst &Point, const SrcState &Cur) {
359434 SrcStatePrinter P (BC);
360435 LLVM_DEBUG ({
@@ -381,11 +456,34 @@ class SrcSafetyAnalysis {
381456 BitVector Clobbered = getClobberedRegs (Point);
382457 SmallVector<MCPhysReg> NewSafeToDerefRegs =
383458 getRegsMadeSafeToDeref (Point, Cur);
459+ SmallVector<MCPhysReg> NewTrustedRegs = getRegsMadeTrusted (Point, Cur);
460+
461+ // Ideally, being trusted is a strictly stronger property than being
462+ // safe-to-dereference. To simplify the computation of Next state, enforce
463+ // this for NewSafeToDerefRegs and NewTrustedRegs. Additionally, this
464+ // fixes the properly for "cumulative" register states in tricky cases
465+ // like the following:
466+ //
467+ // ; LR is safe to dereference here
468+ // mov x16, x30 ; start of the sequence, LR is s-t-d right before
469+ // xpaclri ; clobbers LR, LR is not safe anymore
470+ // cmp x30, x16
471+ // b.eq 1f ; end of the sequence: LR is marked as trusted
472+ // brk 0x1234
473+ // 1:
474+ // ; at this point LR would be marked as trusted,
475+ // ; but not safe-to-dereference
476+ //
477+ for (auto TrustedReg : NewTrustedRegs) {
478+ if (!is_contained (NewSafeToDerefRegs, TrustedReg))
479+ NewSafeToDerefRegs.push_back (TrustedReg);
480+ }
384481
385482 // Then, compute the state after this instruction is executed.
386483 SrcState Next = Cur;
387484
388485 Next.SafeToDerefRegs .reset (Clobbered);
486+ Next.TrustedRegs .reset (Clobbered);
389487 // Keep track of this instruction if it writes to any of the registers we
390488 // need to track that for:
391489 for (MCPhysReg Reg : RegsToTrackInstsFor.getRegisters ())
@@ -406,6 +504,10 @@ class SrcSafetyAnalysis {
406504 lastWritingInsts (Next, Reg).clear ();
407505 }
408506
507+ // Process new trusted registers.
508+ for (MCPhysReg TrustedReg : NewTrustedRegs)
509+ Next.TrustedRegs |= BC.MIB ->getAliases (TrustedReg, /* OnlySmaller=*/ true );
510+
409511 LLVM_DEBUG ({
410512 dbgs () << " .. result: (" ;
411513 P.print (dbgs (), Next);
@@ -462,7 +564,26 @@ class DataflowSrcSafetyAnalysis
462564 return DFParent::getStateBefore (Inst);
463565 }
464566
465- void run () override { DFParent::run (); }
567+ void run () override {
568+ for (BinaryBasicBlock &BB : Func) {
569+ if (auto CheckerInfo = BC.MIB ->getAuthCheckedReg (BB)) {
570+ MCPhysReg CheckedReg = CheckerInfo->first ;
571+ MCInst &FirstInst = *CheckerInfo->second ;
572+ MCInst &LastInst = *BB.getLastNonPseudoInstr ();
573+ LLVM_DEBUG ({
574+ dbgs () << " Found pointer checking sequence in " << BB.getName ()
575+ << " :\n " ;
576+ traceReg (BC, " Checked register" , CheckedReg);
577+ traceInst (BC, " First instruction" , FirstInst);
578+ traceInst (BC, " Last instruction" , LastInst);
579+ });
580+ assert (llvm::any_of (BB, [&](MCInst &I) { return &I == &FirstInst; }) &&
581+ " Data-flow analysis expects the checker not to cross BBs" );
582+ CheckerSequenceInfo[&LastInst] = *CheckerInfo;
583+ }
584+ }
585+ DFParent::run ();
586+ }
466587
467588protected:
468589 void preflight () {}
@@ -658,6 +779,26 @@ shouldReportCallGadget(const BinaryContext &BC, const MCInstReference &Inst,
658779 return std::make_shared<GadgetReport>(CallKind, Inst, DestReg);
659780}
660781
782+ static std::shared_ptr<Report>
783+ shouldReportSigningOracle (const BinaryContext &BC, const MCInstReference &Inst,
784+ const SrcState &S) {
785+ static const GadgetKind SigningOracleKind (" signing oracle found" );
786+
787+ MCPhysReg SignedReg = BC.MIB ->getSignedReg (Inst);
788+ if (SignedReg == BC.MIB ->getNoRegister ())
789+ return nullptr ;
790+
791+ LLVM_DEBUG ({
792+ traceInst (BC, " Found sign inst" , Inst);
793+ traceReg (BC, " Signed reg" , SignedReg);
794+ traceRegMask (BC, " TrustedRegs" , S.TrustedRegs );
795+ });
796+ if (S.TrustedRegs [SignedReg])
797+ return nullptr ;
798+
799+ return std::make_shared<GadgetReport>(SigningOracleKind, Inst, SignedReg);
800+ }
801+
661802template <typename T> static void iterateOverInstrs (BinaryFunction &BF, T Fn) {
662803 if (BF.hasCFG ()) {
663804 for (BinaryBasicBlock &BB : BF)
@@ -702,6 +843,8 @@ Analysis::findGadgets(BinaryFunction &BF,
702843
703844 if (auto Report = shouldReportCallGadget (BC, Inst, S))
704845 Result.Diagnostics .push_back (Report);
846+ if (auto Report = shouldReportSigningOracle (BC, Inst, S))
847+ Result.Diagnostics .push_back (Report);
705848 });
706849 return Result;
707850}
0 commit comments