Skip to content

Commit 59afabd

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 9144462 commit 59afabd

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;
@@ -413,9 +418,8 @@ class SrcSafetyAnalysis {
413418
}
414419

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

420424
// ... an address can be updated in a safe manner, producing the result
421425
// which is as trusted as the input address.
@@ -736,25 +740,28 @@ shouldReportReturnGadget(const BinaryContext &BC, const MCInstReference &Inst,
736740
if (!BC.MIB->isReturn(Inst))
737741
return std::nullopt;
738742

739-
ErrorOr<MCPhysReg> MaybeRetReg = BC.MIB->getRegUsedAsRetDest(Inst);
740-
if (MaybeRetReg.getError()) {
743+
bool IsAuthenticated = false;
744+
std::optional<MCPhysReg> RetReg =
745+
BC.MIB->getRegUsedAsRetDest(Inst, IsAuthenticated);
746+
if (!RetReg) {
741747
return make_generic_report(
742748
Inst, "Warning: pac-ret analysis could not analyze this return "
743749
"instruction");
744750
}
745-
MCPhysReg RetReg = *MaybeRetReg;
751+
if (IsAuthenticated)
752+
return std::nullopt;
753+
754+
assert(*RetReg != BC.MIB->getNoRegister());
746755
LLVM_DEBUG({
747756
traceInst(BC, "Found RET inst", Inst);
748-
traceReg(BC, "RetReg", RetReg);
749-
traceReg(BC, "Authenticated reg", BC.MIB->getAuthenticatedReg(Inst));
757+
traceReg(BC, "RetReg", *RetReg);
758+
traceRegMask(BC, "SafeToDerefRegs", S.SafeToDerefRegs);
750759
});
751-
if (BC.MIB->isAuthenticationOfReg(Inst, RetReg))
752-
return std::nullopt;
753-
LLVM_DEBUG({ traceRegMask(BC, "SafeToDerefRegs", S.SafeToDerefRegs); });
754-
if (S.SafeToDerefRegs[RetReg])
760+
761+
if (S.SafeToDerefRegs[*RetReg])
755762
return std::nullopt;
756763

757-
return make_gadget_report(RetKind, Inst, RetReg);
764+
return make_gadget_report(RetKind, Inst, *RetReg);
758765
}
759766

760767
static std::optional<PartialReport<MCPhysReg>>
@@ -787,19 +794,20 @@ shouldReportSigningOracle(const BinaryContext &BC, const MCInstReference &Inst,
787794
const SrcState &S) {
788795
static const GadgetKind SigningOracleKind("signing oracle found");
789796

790-
MCPhysReg SignedReg = BC.MIB->getSignedReg(Inst);
791-
if (SignedReg == BC.MIB->getNoRegister())
797+
std::optional<MCPhysReg> SignedReg = BC.MIB->getSignedReg(Inst);
798+
if (!SignedReg)
792799
return std::nullopt;
793800

801+
assert(*SignedReg != BC.MIB->getNoRegister());
794802
LLVM_DEBUG({
795803
traceInst(BC, "Found sign inst", Inst);
796-
traceReg(BC, "Signed reg", SignedReg);
804+
traceReg(BC, "Signed reg", *SignedReg);
797805
traceRegMask(BC, "TrustedRegs", S.TrustedRegs);
798806
});
799-
if (S.TrustedRegs[SignedReg])
807+
if (S.TrustedRegs[*SignedReg])
800808
return std::nullopt;
801809

802-
return make_gadget_report(SigningOracleKind, Inst, SignedReg);
810+
return make_gadget_report(SigningOracleKind, Inst, *SignedReg);
803811
}
804812

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

0 commit comments

Comments
 (0)