Skip to content

Commit fa8766c

Browse files
committed
[BOLT] Gadget scanner: clarify MCPlusBuilder callbacks interface
Clarify the semantics of `getAuthenticatedReg` and remove a redundant `isAuthenticationOfReg` method, as combined auth+something instructions (such as `retaa` on AArch64) should be handled carefully, especially when searching for authentication oracles: usually, such instructions cannot be authentication oracles and only some of them actually write an authenticated pointer to a register (such as "ldra x0, [x1]!"). Use `std::optional<MCPhysReg>` returned type instead of plain MCPhysReg and returning `getNoRegister()` as a "not applicable" indication. Document a few existing methods, add information about preconditions.
1 parent 36c3cb9 commit fa8766c

File tree

5 files changed

+130
-94
lines changed

5 files changed

+130
-94
lines changed

bolt/include/bolt/Core/MCPlusBuilder.h

+42-19
Original file line numberDiff line numberDiff line change
@@ -562,30 +562,50 @@ class MCPlusBuilder {
562562
return {};
563563
}
564564

565-
virtual ErrorOr<MCPhysReg> getAuthenticatedReg(const MCInst &Inst) const {
566-
llvm_unreachable("not implemented");
567-
return getNoRegister();
568-
}
569-
570-
virtual bool isAuthenticationOfReg(const MCInst &Inst,
571-
MCPhysReg AuthenticatedReg) const {
565+
/// Returns the register where an authenticated pointer is written to by Inst,
566+
/// or std::nullopt if not authenticating any register.
567+
///
568+
/// Sets IsChecked if the instruction always checks authenticated pointer,
569+
/// i.e. it either returns a successfully authenticated pointer or terminates
570+
/// the program abnormally (such as "ldra x0, [x1]!" on AArch64, which crashes
571+
/// on authentication failure even if FEAT_FPAC is not implemented).
572+
virtual std::optional<MCPhysReg>
573+
getWrittenAuthenticatedReg(const MCInst &Inst, bool &IsChecked) const {
572574
llvm_unreachable("not implemented");
573-
return false;
575+
return std::nullopt;
574576
}
575577

576-
virtual MCPhysReg getSignedReg(const MCInst &Inst) const {
578+
/// Returns the register signed by Inst, or std::nullopt if not signing any
579+
/// register.
580+
///
581+
/// The returned register is assumed to be both input and output operand,
582+
/// as it is done on AArch64.
583+
virtual std::optional<MCPhysReg> getSignedReg(const MCInst &Inst) const {
577584
llvm_unreachable("not implemented");
578-
return getNoRegister();
585+
return std::nullopt;
579586
}
580587

581-
virtual ErrorOr<MCPhysReg> getRegUsedAsRetDest(const MCInst &Inst) const {
588+
/// Returns the register used as a return address. Returns std::nullopt if
589+
/// not applicable, such as reading the return address from a system register
590+
/// or from the stack.
591+
///
592+
/// Sets IsAuthenticatedInternally if the instruction accepts a signed
593+
/// pointer as its operand and authenticates it internally.
594+
///
595+
/// Should only be called when isReturn(Inst) is true.
596+
virtual std::optional<MCPhysReg>
597+
getRegUsedAsRetDest(const MCInst &Inst,
598+
bool &IsAuthenticatedInternally) const {
582599
llvm_unreachable("not implemented");
583-
return getNoRegister();
600+
return std::nullopt;
584601
}
585602

586603
/// Returns the register used as the destination of an indirect branch or call
587604
/// instruction. Sets IsAuthenticatedInternally if the instruction accepts
588605
/// a signed pointer as its operand and authenticates it internally.
606+
///
607+
/// Should only be called if isIndirectCall(Inst) or isIndirectBranch(Inst)
608+
/// returns true.
589609
virtual MCPhysReg
590610
getRegUsedAsIndirectBranchDest(const MCInst &Inst,
591611
bool &IsAuthenticatedInternally) const {
@@ -602,14 +622,14 @@ class MCPlusBuilder {
602622
/// controlled, under the Pointer Authentication threat model.
603623
///
604624
/// If the instruction does not write to any register satisfying the above
605-
/// two conditions, NoRegister is returned.
625+
/// two conditions, std::nullopt is returned.
606626
///
607627
/// The Pointer Authentication threat model assumes an attacker is able to
608628
/// modify any writable memory, but not executable code (due to W^X).
609-
virtual MCPhysReg
629+
virtual std::optional<MCPhysReg>
610630
getMaterializedAddressRegForPtrAuth(const MCInst &Inst) const {
611631
llvm_unreachable("not implemented");
612-
return getNoRegister();
632+
return std::nullopt;
613633
}
614634

615635
/// Analyzes if this instruction can safely perform address arithmetics
@@ -622,10 +642,13 @@ class MCPlusBuilder {
622642
/// controlled, provided InReg and executable code are not. Please note that
623643
/// registers other than InReg as well as the contents of memory which is
624644
/// writable by the process should be considered attacker-controlled.
645+
///
646+
/// The instruction should not write any values derived from InReg anywhere,
647+
/// except for OutReg.
625648
virtual std::optional<std::pair<MCPhysReg, MCPhysReg>>
626649
analyzeAddressArithmeticsForPtrAuth(const MCInst &Inst) const {
627650
llvm_unreachable("not implemented");
628-
return std::make_pair(getNoRegister(), getNoRegister());
651+
return std::nullopt;
629652
}
630653

631654
/// Analyzes if a pointer is checked to be authenticated successfully
@@ -670,10 +693,10 @@ class MCPlusBuilder {
670693
///
671694
/// Use this function for simple, single-instruction patterns instead of
672695
/// its getAuthCheckedReg(BB) counterpart.
673-
virtual MCPhysReg getAuthCheckedReg(const MCInst &Inst,
674-
bool MayOverwrite) const {
696+
virtual std::optional<MCPhysReg> getAuthCheckedReg(const MCInst &Inst,
697+
bool MayOverwrite) const {
675698
llvm_unreachable("not implemented");
676-
return getNoRegister();
699+
return std::nullopt;
677700
}
678701

679702
virtual bool isTerminator(const MCInst &Inst) const;

bolt/lib/Passes/PAuthGadgetScanner.cpp

+36-28
Original file line numberDiff line numberDiff line change
@@ -365,17 +365,15 @@ class SrcSafetyAnalysis {
365365
SmallVector<MCPhysReg> getRegsMadeSafeToDeref(const MCInst &Point,
366366
const SrcState &Cur) const {
367367
SmallVector<MCPhysReg> Regs;
368-
const MCPhysReg NoReg = BC.MIB->getNoRegister();
369368

370369
// A signed pointer can be authenticated, or
371-
ErrorOr<MCPhysReg> AutReg = BC.MIB->getAuthenticatedReg(Point);
372-
if (AutReg && *AutReg != NoReg)
370+
bool Dummy = false;
371+
if (auto AutReg = BC.MIB->getWrittenAuthenticatedReg(Point, Dummy))
373372
Regs.push_back(*AutReg);
374373

375374
// ... a safe address can be materialized, or
376-
MCPhysReg NewAddrReg = BC.MIB->getMaterializedAddressRegForPtrAuth(Point);
377-
if (NewAddrReg != NoReg)
378-
Regs.push_back(NewAddrReg);
375+
if (auto NewAddrReg = BC.MIB->getMaterializedAddressRegForPtrAuth(Point))
376+
Regs.push_back(*NewAddrReg);
379377

380378
// ... an address can be updated in a safe manner, producing the result
381379
// which is as trusted as the input address.
@@ -391,13 +389,20 @@ class SrcSafetyAnalysis {
391389
SmallVector<MCPhysReg> getRegsMadeTrusted(const MCInst &Point,
392390
const SrcState &Cur) const {
393391
SmallVector<MCPhysReg> Regs;
394-
const MCPhysReg NoReg = BC.MIB->getNoRegister();
395392

396393
// An authenticated pointer can be checked, or
397-
MCPhysReg CheckedReg =
394+
std::optional<MCPhysReg> CheckedReg =
398395
BC.MIB->getAuthCheckedReg(Point, /*MayOverwrite=*/false);
399-
if (CheckedReg != NoReg && Cur.SafeToDerefRegs[CheckedReg])
400-
Regs.push_back(CheckedReg);
396+
if (CheckedReg && Cur.SafeToDerefRegs[*CheckedReg])
397+
Regs.push_back(*CheckedReg);
398+
399+
// ... a pointer can be authenticated by an instruction that always checks
400+
// the pointer, or
401+
bool IsChecked = false;
402+
std::optional<MCPhysReg> AutReg =
403+
BC.MIB->getWrittenAuthenticatedReg(Point, IsChecked);
404+
if (AutReg && IsChecked)
405+
Regs.push_back(*AutReg);
401406

402407
if (CheckerSequenceInfo.contains(&Point)) {
403408
MCPhysReg CheckedReg;
@@ -412,9 +417,8 @@ class SrcSafetyAnalysis {
412417
}
413418

414419
// ... a safe address can be materialized, or
415-
MCPhysReg NewAddrReg = BC.MIB->getMaterializedAddressRegForPtrAuth(Point);
416-
if (NewAddrReg != NoReg)
417-
Regs.push_back(NewAddrReg);
420+
if (auto NewAddrReg = BC.MIB->getMaterializedAddressRegForPtrAuth(Point))
421+
Regs.push_back(*NewAddrReg);
418422

419423
// ... an address can be updated in a safe manner, producing the result
420424
// which is as trusted as the input address.
@@ -729,25 +733,28 @@ shouldReportReturnGadget(const BinaryContext &BC, const MCInstReference &Inst,
729733
if (!BC.MIB->isReturn(Inst))
730734
return std::nullopt;
731735

732-
ErrorOr<MCPhysReg> MaybeRetReg = BC.MIB->getRegUsedAsRetDest(Inst);
733-
if (MaybeRetReg.getError()) {
736+
bool IsAuthenticated = false;
737+
std::optional<MCPhysReg> RetReg =
738+
BC.MIB->getRegUsedAsRetDest(Inst, IsAuthenticated);
739+
if (!RetReg) {
734740
return make_generic_report(
735741
Inst, "Warning: pac-ret analysis could not analyze this return "
736742
"instruction");
737743
}
738-
MCPhysReg RetReg = *MaybeRetReg;
744+
if (IsAuthenticated)
745+
return std::nullopt;
746+
747+
assert(*RetReg != BC.MIB->getNoRegister());
739748
LLVM_DEBUG({
740749
traceInst(BC, "Found RET inst", Inst);
741-
traceReg(BC, "RetReg", RetReg);
742-
traceReg(BC, "Authenticated reg", BC.MIB->getAuthenticatedReg(Inst));
750+
traceReg(BC, "RetReg", *RetReg);
751+
traceRegMask(BC, "SafeToDerefRegs", S.SafeToDerefRegs);
743752
});
744-
if (BC.MIB->isAuthenticationOfReg(Inst, RetReg))
745-
return std::nullopt;
746-
LLVM_DEBUG({ traceRegMask(BC, "SafeToDerefRegs", S.SafeToDerefRegs); });
747-
if (S.SafeToDerefRegs[RetReg])
753+
754+
if (S.SafeToDerefRegs[*RetReg])
748755
return std::nullopt;
749756

750-
return make_report(RetKind, Inst, RetReg);
757+
return make_report(RetKind, Inst, *RetReg);
751758
}
752759

753760
static std::optional<BriefReport<MCPhysReg>>
@@ -780,19 +787,20 @@ shouldReportSigningOracle(const BinaryContext &BC, const MCInstReference &Inst,
780787
const SrcState &S) {
781788
static const GadgetKind SigningOracleKind("signing oracle found");
782789

783-
MCPhysReg SignedReg = BC.MIB->getSignedReg(Inst);
784-
if (SignedReg == BC.MIB->getNoRegister())
790+
std::optional<MCPhysReg> SignedReg = BC.MIB->getSignedReg(Inst);
791+
if (!SignedReg)
785792
return std::nullopt;
786793

794+
assert(*SignedReg != BC.MIB->getNoRegister());
787795
LLVM_DEBUG({
788796
traceInst(BC, "Found sign inst", Inst);
789-
traceReg(BC, "Signed reg", SignedReg);
797+
traceReg(BC, "Signed reg", *SignedReg);
790798
traceRegMask(BC, "TrustedRegs", S.TrustedRegs);
791799
});
792-
if (S.TrustedRegs[SignedReg])
800+
if (S.TrustedRegs[*SignedReg])
793801
return std::nullopt;
794802

795-
return make_report(SigningOracleKind, Inst, SignedReg);
803+
return make_report(SigningOracleKind, Inst, *SignedReg);
796804
}
797805

798806
template <typename T> static void iterateOverInstrs(BinaryFunction &BF, T Fn) {

0 commit comments

Comments
 (0)