Skip to content

[BOLT] Gadget scanner: clarify MCPlusBuilder callbacks interface #136147

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: users/atrosinenko/bolt-gs-refactor-reports
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 43 additions & 20 deletions bolt/include/bolt/Core/MCPlusBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -562,35 +562,55 @@ class MCPlusBuilder {
return {};
}

virtual ErrorOr<MCPhysReg> getAuthenticatedReg(const MCInst &Inst) const {
llvm_unreachable("not implemented");
return getNoRegister();
}

virtual bool isAuthenticationOfReg(const MCInst &Inst,
MCPhysReg AuthenticatedReg) const {
/// Returns the register where an authenticated pointer is written to by Inst,
/// or std::nullopt if not authenticating any register.
///
/// Sets IsChecked if the instruction always checks authenticated pointer,
/// i.e. it either writes a successfully authenticated pointer or terminates
/// the program abnormally (such as "ldra x0, [x1]!" on AArch64, which crashes
/// on authentication failure even if FEAT_FPAC is not implemented).
virtual std::optional<MCPhysReg>
getWrittenAuthenticatedReg(const MCInst &Inst, bool &IsChecked) const {
llvm_unreachable("not implemented");
return false;
return std::nullopt;
}

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

virtual ErrorOr<MCPhysReg> getRegUsedAsRetDest(const MCInst &Inst) const {
/// Returns the register used as a return address. Returns std::nullopt if
/// not applicable, such as reading the return address from a system register
/// or from the stack.
///
/// Sets IsAuthenticatedInternally if the instruction accepts a signed
/// pointer as its operand and authenticates it internally.
///
/// Should only be called when isReturn(Inst) is true.
virtual std::optional<MCPhysReg>
getRegUsedAsRetDest(const MCInst &Inst,
bool &IsAuthenticatedInternally) const {
llvm_unreachable("not implemented");
return getNoRegister();
return std::nullopt;
}

/// Returns the register used as the destination of an indirect branch or call
/// instruction. Sets IsAuthenticatedInternally if the instruction accepts
/// a signed pointer as its operand and authenticates it internally.
///
/// Should only be called if isIndirectCall(Inst) or isIndirectBranch(Inst)
/// returns true.
virtual MCPhysReg
getRegUsedAsIndirectBranchDest(const MCInst &Inst,
bool &IsAuthenticatedInternally) const {
llvm_unreachable("not implemented");
return getNoRegister();
return 0;
}

/// Returns the register containing an address safely materialized by `Inst`
Expand All @@ -602,14 +622,14 @@ class MCPlusBuilder {
/// controlled, under the Pointer Authentication threat model.
///
/// If the instruction does not write to any register satisfying the above
/// two conditions, NoRegister is returned.
/// two conditions, std::nullopt is returned.
///
/// The Pointer Authentication threat model assumes an attacker is able to
/// modify any writable memory, but not executable code (due to W^X).
virtual MCPhysReg
virtual std::optional<MCPhysReg>
getMaterializedAddressRegForPtrAuth(const MCInst &Inst) const {
llvm_unreachable("not implemented");
return getNoRegister();
return std::nullopt;
}

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

/// Analyzes if a pointer is checked to be authenticated successfully
Expand Down Expand Up @@ -670,10 +693,10 @@ class MCPlusBuilder {
///
/// Use this function for simple, single-instruction patterns instead of
/// its getAuthCheckedReg(BB) counterpart.
virtual MCPhysReg getAuthCheckedReg(const MCInst &Inst,
bool MayOverwrite) const {
virtual std::optional<MCPhysReg> getAuthCheckedReg(const MCInst &Inst,
bool MayOverwrite) const {
llvm_unreachable("not implemented");
return getNoRegister();
return std::nullopt;
}

virtual bool isTerminator(const MCInst &Inst) const;
Expand Down
64 changes: 36 additions & 28 deletions bolt/lib/Passes/PAuthGadgetScanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -365,17 +365,15 @@ class SrcSafetyAnalysis {
SmallVector<MCPhysReg> getRegsMadeSafeToDeref(const MCInst &Point,
const SrcState &Cur) const {
SmallVector<MCPhysReg> Regs;
const MCPhysReg NoReg = BC.MIB->getNoRegister();

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

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

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

// An authenticated pointer can be checked, or
MCPhysReg CheckedReg =
std::optional<MCPhysReg> CheckedReg =
BC.MIB->getAuthCheckedReg(Point, /*MayOverwrite=*/false);
if (CheckedReg != NoReg && Cur.SafeToDerefRegs[CheckedReg])
Regs.push_back(CheckedReg);
if (CheckedReg && Cur.SafeToDerefRegs[*CheckedReg])
Regs.push_back(*CheckedReg);

// ... a pointer can be authenticated by an instruction that always checks
// the pointer, or
bool IsChecked = false;
std::optional<MCPhysReg> AutReg =
BC.MIB->getWrittenAuthenticatedReg(Point, IsChecked);
if (AutReg && IsChecked)
Regs.push_back(*AutReg);

if (CheckerSequenceInfo.contains(&Point)) {
MCPhysReg CheckedReg;
Expand All @@ -413,9 +418,8 @@ class SrcSafetyAnalysis {
}

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

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

ErrorOr<MCPhysReg> MaybeRetReg = BC.MIB->getRegUsedAsRetDest(Inst);
if (MaybeRetReg.getError()) {
bool IsAuthenticated = false;
std::optional<MCPhysReg> RetReg =
BC.MIB->getRegUsedAsRetDest(Inst, IsAuthenticated);
if (!RetReg) {
return make_generic_report(
Inst, "Warning: pac-ret analysis could not analyze this return "
"instruction");
}
MCPhysReg RetReg = *MaybeRetReg;
if (IsAuthenticated)
return std::nullopt;

assert(*RetReg != BC.MIB->getNoRegister());
LLVM_DEBUG({
traceInst(BC, "Found RET inst", Inst);
traceReg(BC, "RetReg", RetReg);
traceReg(BC, "Authenticated reg", BC.MIB->getAuthenticatedReg(Inst));
traceReg(BC, "RetReg", *RetReg);
traceRegMask(BC, "SafeToDerefRegs", S.SafeToDerefRegs);
});
if (BC.MIB->isAuthenticationOfReg(Inst, RetReg))
return std::nullopt;
LLVM_DEBUG({ traceRegMask(BC, "SafeToDerefRegs", S.SafeToDerefRegs); });
if (S.SafeToDerefRegs[RetReg])

if (S.SafeToDerefRegs[*RetReg])
return std::nullopt;

return make_gadget_report(RetKind, Inst, RetReg);
return make_gadget_report(RetKind, Inst, *RetReg);
}

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

MCPhysReg SignedReg = BC.MIB->getSignedReg(Inst);
if (SignedReg == BC.MIB->getNoRegister())
std::optional<MCPhysReg> SignedReg = BC.MIB->getSignedReg(Inst);
if (!SignedReg)
return std::nullopt;

assert(*SignedReg != BC.MIB->getNoRegister());
LLVM_DEBUG({
traceInst(BC, "Found sign inst", Inst);
traceReg(BC, "Signed reg", SignedReg);
traceReg(BC, "Signed reg", *SignedReg);
traceRegMask(BC, "TrustedRegs", S.TrustedRegs);
});
if (S.TrustedRegs[SignedReg])
if (S.TrustedRegs[*SignedReg])
return std::nullopt;

return make_gadget_report(SigningOracleKind, Inst, SignedReg);
return make_gadget_report(SigningOracleKind, Inst, *SignedReg);
}

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