[BOLT] Gadget scanner: detect signing oracles (#134146)
Implement the detection of signing oracles. In this patch, a signing oracle is defined as a sign instruction that accepts a "non-protected" pointer, but for a slightly different definition of "non-protected" compared to control flow instructions. A second BitVector named TrustedRegs is added to the register state computed by the data-flow analysis. The difference between a "safe-to-dereference" and a "trusted" register states is that to make an unsafe register trusted by authentication, one has to make sure that the authentication succeeded. For example, on AArch64 without FEAT_PAuth2 and FEAT_EPAC, an authentication instruction produces an invalid pointer on failure, so that subsequent memory access triggers an error, but re-signing such pointer would "fix" the signature. Note that while a separate "trusted" register state may be redundant depending on the specific semantics of auth and sign operations, it is still important to check signing operations: while code like this resign: autda x0, x1 pacda x0, x2 ret is probably safe provided `autda` generates an error on authentication failure, this function sign_anything: pacda x0, x1 ret is inherently unsafe.
This commit is contained in:
parent
67440f0b83
commit
48a2836b4d
@ -49,6 +49,7 @@ class MCSymbol;
|
||||
class raw_ostream;
|
||||
|
||||
namespace bolt {
|
||||
class BinaryBasicBlock;
|
||||
class BinaryFunction;
|
||||
|
||||
/// Different types of indirect branches encountered during disassembly.
|
||||
@ -572,6 +573,11 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual MCPhysReg getSignedReg(const MCInst &Inst) const {
|
||||
llvm_unreachable("not implemented");
|
||||
return getNoRegister();
|
||||
}
|
||||
|
||||
virtual ErrorOr<MCPhysReg> getRegUsedAsRetDest(const MCInst &Inst) const {
|
||||
llvm_unreachable("not implemented");
|
||||
return getNoRegister();
|
||||
@ -622,6 +628,54 @@ public:
|
||||
return std::make_pair(getNoRegister(), getNoRegister());
|
||||
}
|
||||
|
||||
/// Analyzes if a pointer is checked to be authenticated successfully
|
||||
/// by the end of the basic block.
|
||||
///
|
||||
/// It is possible for pointer authentication instructions not to terminate
|
||||
/// the program abnormally on authentication failure and return some invalid
|
||||
/// pointer instead (like it is done on AArch64 when FEAT_FPAC is not
|
||||
/// implemented). This might be enough to crash on invalid memory access when
|
||||
/// the pointer is later used as the destination of a load, store, or branch
|
||||
/// instruction. On the other hand, when the pointer is not used right away,
|
||||
/// it may be important for the compiler to check the address explicitly not
|
||||
/// to introduce a signing or authentication oracle.
|
||||
///
|
||||
/// This function is intended to detect a complex, multi-instruction pointer-
|
||||
/// checking sequence spanning a contiguous range of instructions at the end
|
||||
/// of the basic block (as these sequences are expected to end with a
|
||||
/// conditional branch - this is how they are implemented on AArch64 by LLVM).
|
||||
/// If a (Reg, FirstInst) pair is returned and before execution of FirstInst
|
||||
/// Reg was last written to by an authentication instruction, then it is known
|
||||
/// that in any successor of BB either
|
||||
/// * the authentication instruction that last wrote to Reg succeeded, or
|
||||
/// * the program is terminated abnormally without introducing any signing
|
||||
/// or authentication oracles
|
||||
///
|
||||
/// Note that this function is not expected to repeat the results returned
|
||||
/// by getAuthCheckedReg(Inst, MayOverwrite) function below.
|
||||
virtual std::optional<std::pair<MCPhysReg, MCInst *>>
|
||||
getAuthCheckedReg(BinaryBasicBlock &BB) const {
|
||||
llvm_unreachable("not implemented");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/// Returns the register that is checked to be authenticated successfully.
|
||||
///
|
||||
/// If the returned register was last written to by an authentication
|
||||
/// instruction and that authentication failed, then the program is known
|
||||
/// to be terminated abnormally as a result of execution of Inst.
|
||||
///
|
||||
/// Additionally, if MayOverwrite is false, it is known that the authenticated
|
||||
/// pointer is not clobbered by Inst itself.
|
||||
///
|
||||
/// Use this function for simple, single-instruction patterns instead of
|
||||
/// its getAuthCheckedReg(BB) counterpart.
|
||||
virtual MCPhysReg getAuthCheckedReg(const MCInst &Inst,
|
||||
bool MayOverwrite) const {
|
||||
llvm_unreachable("not implemented");
|
||||
return getNoRegister();
|
||||
}
|
||||
|
||||
virtual bool isTerminator(const MCInst &Inst) const;
|
||||
|
||||
virtual bool isNoop(const MCInst &Inst) const {
|
||||
|
@ -173,10 +173,24 @@ public:
|
||||
/// X30 is safe-to-dereference - the state computed for sub- and
|
||||
/// super-registers is not inspected.
|
||||
struct SrcState {
|
||||
/// A BitVector containing the registers that are either safe at function
|
||||
/// entry and were not clobbered yet, or those not clobbered since being
|
||||
/// authenticated.
|
||||
/// A BitVector containing the registers that are either authenticated
|
||||
/// (assuming failed authentication is permitted to produce an invalid
|
||||
/// address, provided it generates an error on memory access) or whose
|
||||
/// value is known not to be attacker-controlled under Pointer Authentication
|
||||
/// threat model. The registers in this set are either
|
||||
/// * not clobbered since being authenticated, or
|
||||
/// * trusted at function entry and were not clobbered yet, or
|
||||
/// * contain a safely materialized address.
|
||||
BitVector SafeToDerefRegs;
|
||||
/// A BitVector containing the registers that are either authenticated
|
||||
/// *successfully* or whose value is known not to be attacker-controlled
|
||||
/// under Pointer Authentication threat model.
|
||||
/// The registers in this set are either
|
||||
/// * authenticated and then checked to be authenticated successfully
|
||||
/// (and not clobbered since then), or
|
||||
/// * trusted at function entry and were not clobbered yet, or
|
||||
/// * contain a safely materialized address.
|
||||
BitVector TrustedRegs;
|
||||
/// A vector of sets, only used in the second data flow run.
|
||||
/// Each element in the vector represents one of the registers for which we
|
||||
/// track the set of last instructions that wrote to this register. For
|
||||
@ -189,7 +203,8 @@ struct SrcState {
|
||||
SrcState() {}
|
||||
|
||||
SrcState(unsigned NumRegs, unsigned NumRegsToTrack)
|
||||
: SafeToDerefRegs(NumRegs), LastInstWritingReg(NumRegsToTrack) {}
|
||||
: SafeToDerefRegs(NumRegs), TrustedRegs(NumRegs),
|
||||
LastInstWritingReg(NumRegsToTrack) {}
|
||||
|
||||
SrcState &merge(const SrcState &StateIn) {
|
||||
if (StateIn.empty())
|
||||
@ -198,6 +213,7 @@ struct SrcState {
|
||||
return (*this = StateIn);
|
||||
|
||||
SafeToDerefRegs &= StateIn.SafeToDerefRegs;
|
||||
TrustedRegs &= StateIn.TrustedRegs;
|
||||
for (unsigned I = 0; I < LastInstWritingReg.size(); ++I)
|
||||
for (const MCInst *J : StateIn.LastInstWritingReg[I])
|
||||
LastInstWritingReg[I].insert(J);
|
||||
@ -210,6 +226,7 @@ struct SrcState {
|
||||
|
||||
bool operator==(const SrcState &RHS) const {
|
||||
return SafeToDerefRegs == RHS.SafeToDerefRegs &&
|
||||
TrustedRegs == RHS.TrustedRegs &&
|
||||
LastInstWritingReg == RHS.LastInstWritingReg;
|
||||
}
|
||||
bool operator!=(const SrcState &RHS) const { return !((*this) == RHS); }
|
||||
@ -234,6 +251,7 @@ raw_ostream &operator<<(raw_ostream &OS, const SrcState &S) {
|
||||
OS << "empty";
|
||||
} else {
|
||||
OS << "SafeToDerefRegs: " << S.SafeToDerefRegs << ", ";
|
||||
OS << "TrustedRegs: " << S.TrustedRegs << ", ";
|
||||
printLastInsts(OS, S.LastInstWritingReg);
|
||||
}
|
||||
OS << ">";
|
||||
@ -254,18 +272,22 @@ void SrcStatePrinter::print(raw_ostream &OS, const SrcState &S) const {
|
||||
OS << "src-state<";
|
||||
if (S.empty()) {
|
||||
assert(S.SafeToDerefRegs.empty());
|
||||
assert(S.TrustedRegs.empty());
|
||||
assert(S.LastInstWritingReg.empty());
|
||||
OS << "empty";
|
||||
} else {
|
||||
OS << "SafeToDerefRegs: ";
|
||||
RegStatePrinter.print(OS, S.SafeToDerefRegs);
|
||||
OS << ", TrustedRegs: ";
|
||||
RegStatePrinter.print(OS, S.TrustedRegs);
|
||||
OS << ", ";
|
||||
printLastInsts(OS, S.LastInstWritingReg);
|
||||
}
|
||||
OS << ">";
|
||||
}
|
||||
|
||||
/// Computes which registers are safe to be used by control flow instructions.
|
||||
/// Computes which registers are safe to be used by control flow and signing
|
||||
/// instructions.
|
||||
///
|
||||
/// This is the base class for two implementations: a dataflow-based analysis
|
||||
/// which is intended to be used for most functions and a simplified CFG-unaware
|
||||
@ -293,6 +315,17 @@ protected:
|
||||
/// RegToTrackInstsFor is the set of registers for which the dataflow analysis
|
||||
/// must compute which the last set of instructions writing to it are.
|
||||
const TrackedRegisters RegsToTrackInstsFor;
|
||||
/// Stores information about the detected instruction sequences emitted to
|
||||
/// check an authenticated pointer. Specifically, if such sequence is detected
|
||||
/// in a basic block, it maps the last instruction of that basic block to
|
||||
/// (CheckedRegister, FirstInstOfTheSequence) pair, see the description of
|
||||
/// MCPlusBuilder::getAuthCheckedReg(BB) method.
|
||||
///
|
||||
/// As the detection of such sequences requires iterating over the adjacent
|
||||
/// instructions, it should be done before calling computeNext(), which
|
||||
/// operates on separate instructions.
|
||||
DenseMap<const MCInst *, std::pair<MCPhysReg, const MCInst *>>
|
||||
CheckerSequenceInfo;
|
||||
|
||||
SmallPtrSet<const MCInst *, 4> &lastWritingInsts(SrcState &S,
|
||||
MCPhysReg Reg) const {
|
||||
@ -308,7 +341,8 @@ protected:
|
||||
SrcState createEntryState() {
|
||||
SrcState S(NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters());
|
||||
for (MCPhysReg Reg : BC.MIB->getTrustedLiveInRegs())
|
||||
S.SafeToDerefRegs |= BC.MIB->getAliases(Reg, /*OnlySmaller=*/true);
|
||||
S.TrustedRegs |= BC.MIB->getAliases(Reg, /*OnlySmaller=*/true);
|
||||
S.SafeToDerefRegs = S.TrustedRegs;
|
||||
return S;
|
||||
}
|
||||
|
||||
@ -355,6 +389,47 @@ protected:
|
||||
return Regs;
|
||||
}
|
||||
|
||||
// Returns all registers made trusted by this instruction.
|
||||
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 =
|
||||
BC.MIB->getAuthCheckedReg(Point, /*MayOverwrite=*/false);
|
||||
if (CheckedReg != NoReg && Cur.SafeToDerefRegs[CheckedReg])
|
||||
Regs.push_back(CheckedReg);
|
||||
|
||||
if (CheckerSequenceInfo.contains(&Point)) {
|
||||
MCPhysReg CheckedReg;
|
||||
const MCInst *FirstCheckerInst;
|
||||
std::tie(CheckedReg, FirstCheckerInst) = CheckerSequenceInfo.at(&Point);
|
||||
|
||||
// FirstCheckerInst should belong to the same basic block (see the
|
||||
// assertion in DataflowSrcSafetyAnalysis::run()), meaning it was
|
||||
// deterministically processed a few steps before this instruction.
|
||||
const SrcState &StateBeforeChecker =
|
||||
getStateBefore(*FirstCheckerInst).get();
|
||||
if (StateBeforeChecker.SafeToDerefRegs[CheckedReg])
|
||||
Regs.push_back(CheckedReg);
|
||||
}
|
||||
|
||||
// ... a safe address can be materialized, or
|
||||
MCPhysReg NewAddrReg = BC.MIB->getMaterializedAddressRegForPtrAuth(Point);
|
||||
if (NewAddrReg != NoReg)
|
||||
Regs.push_back(NewAddrReg);
|
||||
|
||||
// ... an address can be updated in a safe manner, producing the result
|
||||
// which is as trusted as the input address.
|
||||
if (auto DstAndSrc = BC.MIB->analyzeAddressArithmeticsForPtrAuth(Point)) {
|
||||
if (Cur.TrustedRegs[DstAndSrc->second])
|
||||
Regs.push_back(DstAndSrc->first);
|
||||
}
|
||||
|
||||
return Regs;
|
||||
}
|
||||
|
||||
SrcState computeNext(const MCInst &Point, const SrcState &Cur) {
|
||||
SrcStatePrinter P(BC);
|
||||
LLVM_DEBUG({
|
||||
@ -381,11 +456,34 @@ protected:
|
||||
BitVector Clobbered = getClobberedRegs(Point);
|
||||
SmallVector<MCPhysReg> NewSafeToDerefRegs =
|
||||
getRegsMadeSafeToDeref(Point, Cur);
|
||||
SmallVector<MCPhysReg> NewTrustedRegs = getRegsMadeTrusted(Point, Cur);
|
||||
|
||||
// Ideally, being trusted is a strictly stronger property than being
|
||||
// safe-to-dereference. To simplify the computation of Next state, enforce
|
||||
// this for NewSafeToDerefRegs and NewTrustedRegs. Additionally, this
|
||||
// fixes the properly for "cumulative" register states in tricky cases
|
||||
// like the following:
|
||||
//
|
||||
// ; LR is safe to dereference here
|
||||
// mov x16, x30 ; start of the sequence, LR is s-t-d right before
|
||||
// xpaclri ; clobbers LR, LR is not safe anymore
|
||||
// cmp x30, x16
|
||||
// b.eq 1f ; end of the sequence: LR is marked as trusted
|
||||
// brk 0x1234
|
||||
// 1:
|
||||
// ; at this point LR would be marked as trusted,
|
||||
// ; but not safe-to-dereference
|
||||
//
|
||||
for (auto TrustedReg : NewTrustedRegs) {
|
||||
if (!is_contained(NewSafeToDerefRegs, TrustedReg))
|
||||
NewSafeToDerefRegs.push_back(TrustedReg);
|
||||
}
|
||||
|
||||
// Then, compute the state after this instruction is executed.
|
||||
SrcState Next = Cur;
|
||||
|
||||
Next.SafeToDerefRegs.reset(Clobbered);
|
||||
Next.TrustedRegs.reset(Clobbered);
|
||||
// Keep track of this instruction if it writes to any of the registers we
|
||||
// need to track that for:
|
||||
for (MCPhysReg Reg : RegsToTrackInstsFor.getRegisters())
|
||||
@ -406,6 +504,10 @@ protected:
|
||||
lastWritingInsts(Next, Reg).clear();
|
||||
}
|
||||
|
||||
// Process new trusted registers.
|
||||
for (MCPhysReg TrustedReg : NewTrustedRegs)
|
||||
Next.TrustedRegs |= BC.MIB->getAliases(TrustedReg, /*OnlySmaller=*/true);
|
||||
|
||||
LLVM_DEBUG({
|
||||
dbgs() << " .. result: (";
|
||||
P.print(dbgs(), Next);
|
||||
@ -462,7 +564,26 @@ public:
|
||||
return DFParent::getStateBefore(Inst);
|
||||
}
|
||||
|
||||
void run() override { DFParent::run(); }
|
||||
void run() override {
|
||||
for (BinaryBasicBlock &BB : Func) {
|
||||
if (auto CheckerInfo = BC.MIB->getAuthCheckedReg(BB)) {
|
||||
MCPhysReg CheckedReg = CheckerInfo->first;
|
||||
MCInst &FirstInst = *CheckerInfo->second;
|
||||
MCInst &LastInst = *BB.getLastNonPseudoInstr();
|
||||
LLVM_DEBUG({
|
||||
dbgs() << "Found pointer checking sequence in " << BB.getName()
|
||||
<< ":\n";
|
||||
traceReg(BC, "Checked register", CheckedReg);
|
||||
traceInst(BC, "First instruction", FirstInst);
|
||||
traceInst(BC, "Last instruction", LastInst);
|
||||
});
|
||||
assert(llvm::any_of(BB, [&](MCInst &I) { return &I == &FirstInst; }) &&
|
||||
"Data-flow analysis expects the checker not to cross BBs");
|
||||
CheckerSequenceInfo[&LastInst] = *CheckerInfo;
|
||||
}
|
||||
}
|
||||
DFParent::run();
|
||||
}
|
||||
|
||||
protected:
|
||||
void preflight() {}
|
||||
@ -658,6 +779,26 @@ shouldReportCallGadget(const BinaryContext &BC, const MCInstReference &Inst,
|
||||
return std::make_shared<GadgetReport>(CallKind, Inst, DestReg);
|
||||
}
|
||||
|
||||
static std::shared_ptr<Report>
|
||||
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())
|
||||
return nullptr;
|
||||
|
||||
LLVM_DEBUG({
|
||||
traceInst(BC, "Found sign inst", Inst);
|
||||
traceReg(BC, "Signed reg", SignedReg);
|
||||
traceRegMask(BC, "TrustedRegs", S.TrustedRegs);
|
||||
});
|
||||
if (S.TrustedRegs[SignedReg])
|
||||
return nullptr;
|
||||
|
||||
return std::make_shared<GadgetReport>(SigningOracleKind, Inst, SignedReg);
|
||||
}
|
||||
|
||||
template <typename T> static void iterateOverInstrs(BinaryFunction &BF, T Fn) {
|
||||
if (BF.hasCFG()) {
|
||||
for (BinaryBasicBlock &BB : BF)
|
||||
@ -702,6 +843,8 @@ Analysis::findGadgets(BinaryFunction &BF,
|
||||
|
||||
if (auto Report = shouldReportCallGadget(BC, Inst, S))
|
||||
Result.Diagnostics.push_back(Report);
|
||||
if (auto Report = shouldReportSigningOracle(BC, Inst, S))
|
||||
Result.Diagnostics.push_back(Report);
|
||||
});
|
||||
return Result;
|
||||
}
|
||||
|
@ -257,6 +257,36 @@ public:
|
||||
return AuthenticatedReg.getError() ? false : *AuthenticatedReg == Reg;
|
||||
}
|
||||
|
||||
MCPhysReg getSignedReg(const MCInst &Inst) const override {
|
||||
switch (Inst.getOpcode()) {
|
||||
case AArch64::PACIA:
|
||||
case AArch64::PACIB:
|
||||
case AArch64::PACDA:
|
||||
case AArch64::PACDB:
|
||||
case AArch64::PACIZA:
|
||||
case AArch64::PACIZB:
|
||||
case AArch64::PACDZA:
|
||||
case AArch64::PACDZB:
|
||||
return Inst.getOperand(0).getReg();
|
||||
case AArch64::PACIAZ:
|
||||
case AArch64::PACIBZ:
|
||||
case AArch64::PACIASP:
|
||||
case AArch64::PACIBSP:
|
||||
case AArch64::PACIASPPC:
|
||||
case AArch64::PACIBSPPC:
|
||||
case AArch64::PACNBIASPPC:
|
||||
case AArch64::PACNBIBSPPC:
|
||||
return AArch64::LR;
|
||||
case AArch64::PACIA1716:
|
||||
case AArch64::PACIB1716:
|
||||
case AArch64::PACIA171615:
|
||||
case AArch64::PACIB171615:
|
||||
return AArch64::X17;
|
||||
default:
|
||||
return getNoRegister();
|
||||
}
|
||||
}
|
||||
|
||||
ErrorOr<MCPhysReg> getRegUsedAsRetDest(const MCInst &Inst) const override {
|
||||
assert(isReturn(Inst));
|
||||
switch (Inst.getOpcode()) {
|
||||
@ -339,6 +369,204 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::pair<MCPhysReg, MCInst *>>
|
||||
getAuthCheckedReg(BinaryBasicBlock &BB) const override {
|
||||
// Match several possible hard-coded sequences of instructions which can be
|
||||
// emitted by LLVM backend to check that the authenticated pointer is
|
||||
// correct (see AArch64AsmPrinter::emitPtrauthCheckAuthenticatedValue).
|
||||
//
|
||||
// This function only matches sequences involving branch instructions.
|
||||
// All these sequences have the form:
|
||||
//
|
||||
// (0) ... regular code that authenticates a pointer in Xn ...
|
||||
// (1) analyze Xn
|
||||
// (2) branch to .Lon_success if the pointer is correct
|
||||
// (3) BRK #imm (fall-through basic block)
|
||||
//
|
||||
// In the above pseudocode, (1) + (2) is one of the following sequences:
|
||||
//
|
||||
// - eor Xtmp, Xn, Xn, lsl #1
|
||||
// tbz Xtmp, #62, .Lon_success
|
||||
//
|
||||
// - mov Xtmp, Xn
|
||||
// xpac(i|d) Xn (or xpaclri if Xn is LR)
|
||||
// cmp Xtmp, Xn
|
||||
// b.eq .Lon_success
|
||||
//
|
||||
// Note that any branch destination operand is accepted as .Lon_success -
|
||||
// it is the responsibility of the caller of getAuthCheckedReg to inspect
|
||||
// the list of successors of this basic block as appropriate.
|
||||
|
||||
// Any of the above code sequences assume the fall-through basic block
|
||||
// is a dead-end BRK instruction (any immediate operand is accepted).
|
||||
const BinaryBasicBlock *BreakBB = BB.getFallthrough();
|
||||
if (!BreakBB || BreakBB->empty() ||
|
||||
BreakBB->front().getOpcode() != AArch64::BRK)
|
||||
return std::nullopt;
|
||||
|
||||
// Iterate over the instructions of BB in reverse order, matching opcodes
|
||||
// and operands.
|
||||
MCPhysReg TestedReg = 0;
|
||||
MCPhysReg ScratchReg = 0;
|
||||
auto It = BB.end();
|
||||
auto StepAndGetOpcode = [&It, &BB]() -> int {
|
||||
if (It == BB.begin())
|
||||
return -1;
|
||||
--It;
|
||||
return It->getOpcode();
|
||||
};
|
||||
|
||||
switch (StepAndGetOpcode()) {
|
||||
default:
|
||||
// Not matched the branch instruction.
|
||||
return std::nullopt;
|
||||
case AArch64::Bcc:
|
||||
// Bcc EQ, .Lon_success
|
||||
if (It->getOperand(0).getImm() != AArch64CC::EQ)
|
||||
return std::nullopt;
|
||||
// Not checking .Lon_success (see above).
|
||||
|
||||
// SUBSXrs XZR, TestedReg, ScratchReg, 0 (used by "CMP reg, reg" alias)
|
||||
if (StepAndGetOpcode() != AArch64::SUBSXrs ||
|
||||
It->getOperand(0).getReg() != AArch64::XZR ||
|
||||
It->getOperand(3).getImm() != 0)
|
||||
return std::nullopt;
|
||||
TestedReg = It->getOperand(1).getReg();
|
||||
ScratchReg = It->getOperand(2).getReg();
|
||||
|
||||
// Either XPAC(I|D) ScratchReg, ScratchReg
|
||||
// or XPACLRI
|
||||
switch (StepAndGetOpcode()) {
|
||||
default:
|
||||
return std::nullopt;
|
||||
case AArch64::XPACLRI:
|
||||
// No operands to check, but using XPACLRI forces TestedReg to be X30.
|
||||
if (TestedReg != AArch64::LR)
|
||||
return std::nullopt;
|
||||
break;
|
||||
case AArch64::XPACI:
|
||||
case AArch64::XPACD:
|
||||
if (It->getOperand(0).getReg() != ScratchReg ||
|
||||
It->getOperand(1).getReg() != ScratchReg)
|
||||
return std::nullopt;
|
||||
break;
|
||||
}
|
||||
|
||||
// ORRXrs ScratchReg, XZR, TestedReg, 0 (used by "MOV reg, reg" alias)
|
||||
if (StepAndGetOpcode() != AArch64::ORRXrs)
|
||||
return std::nullopt;
|
||||
if (It->getOperand(0).getReg() != ScratchReg ||
|
||||
It->getOperand(1).getReg() != AArch64::XZR ||
|
||||
It->getOperand(2).getReg() != TestedReg ||
|
||||
It->getOperand(3).getImm() != 0)
|
||||
return std::nullopt;
|
||||
|
||||
return std::make_pair(TestedReg, &*It);
|
||||
|
||||
case AArch64::TBZX:
|
||||
// TBZX ScratchReg, 62, .Lon_success
|
||||
ScratchReg = It->getOperand(0).getReg();
|
||||
if (It->getOperand(1).getImm() != 62)
|
||||
return std::nullopt;
|
||||
// Not checking .Lon_success (see above).
|
||||
|
||||
// EORXrs ScratchReg, TestedReg, TestedReg, 1
|
||||
if (StepAndGetOpcode() != AArch64::EORXrs)
|
||||
return std::nullopt;
|
||||
TestedReg = It->getOperand(1).getReg();
|
||||
if (It->getOperand(0).getReg() != ScratchReg ||
|
||||
It->getOperand(2).getReg() != TestedReg ||
|
||||
It->getOperand(3).getImm() != 1)
|
||||
return std::nullopt;
|
||||
|
||||
return std::make_pair(TestedReg, &*It);
|
||||
}
|
||||
}
|
||||
|
||||
MCPhysReg getAuthCheckedReg(const MCInst &Inst,
|
||||
bool MayOverwrite) const override {
|
||||
// Cannot trivially reuse AArch64InstrInfo::getMemOperandWithOffsetWidth()
|
||||
// method as it accepts an instance of MachineInstr, not MCInst.
|
||||
const MCInstrDesc &Desc = Info->get(Inst.getOpcode());
|
||||
|
||||
// If signing oracles are considered, the particular value left in the base
|
||||
// register after this instruction is important. This function checks that
|
||||
// if the base register was overwritten, it is due to address write-back:
|
||||
//
|
||||
// ; good:
|
||||
// autdza x1 ; x1 is authenticated (may fail)
|
||||
// ldr x0, [x1, #8] ; x1 is checked and not changed
|
||||
// pacdzb x1
|
||||
//
|
||||
// ; also good:
|
||||
// autdza x1
|
||||
// ldr x0, [x1, #8]! ; x1 is checked and incremented by 8
|
||||
// pacdzb x1
|
||||
//
|
||||
// ; bad (the value being signed is not the authenticated one):
|
||||
// autdza x1
|
||||
// ldr x1, [x1, #8] ; x1 is overwritten with an unrelated value
|
||||
// pacdzb x1
|
||||
//
|
||||
// ; also bad:
|
||||
// autdza x1
|
||||
// pacdzb x1 ; possibly signing the result of failed authentication
|
||||
//
|
||||
// Note that this function is not needed for authentication oracles, as the
|
||||
// particular value left in the register after a successful memory access
|
||||
// is not important.
|
||||
auto ClobbersBaseRegExceptWriteback = [&](unsigned BaseRegUseIndex) {
|
||||
// FIXME: Compute the indices of address operands (base reg and written-
|
||||
// back result) in AArch64InstrInfo instead of this ad-hoc code.
|
||||
MCPhysReg BaseReg = Inst.getOperand(BaseRegUseIndex).getReg();
|
||||
unsigned WrittenBackDefIndex = Desc.getOperandConstraint(
|
||||
BaseRegUseIndex, MCOI::OperandConstraint::TIED_TO);
|
||||
|
||||
for (unsigned DefIndex = 0; DefIndex < Desc.getNumDefs(); ++DefIndex) {
|
||||
// Address write-back is permitted:
|
||||
//
|
||||
// autda x0, x2
|
||||
// ; x0 is authenticated
|
||||
// ldr x1, [x0, #8]!
|
||||
// ; x0 is trusted (as authenticated and checked)
|
||||
if (DefIndex == WrittenBackDefIndex)
|
||||
continue;
|
||||
|
||||
// Any other overwriting is not permitted:
|
||||
//
|
||||
// autda x0, x2
|
||||
// ; x0 is authenticated
|
||||
// ldr w0, [x0]
|
||||
// ; x0 is not authenticated anymore
|
||||
if (RegInfo->regsOverlap(Inst.getOperand(DefIndex).getReg(), BaseReg))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
if (mayLoad(Inst)) {
|
||||
// The first Use operand is the base address register.
|
||||
unsigned BaseRegIndex = Desc.getNumDefs();
|
||||
|
||||
// Reject non-immediate offsets, as adding a 64-bit register can change
|
||||
// the resulting address arbitrarily.
|
||||
for (unsigned I = BaseRegIndex + 1, E = Desc.getNumOperands(); I < E; ++I)
|
||||
if (Inst.getOperand(I).isReg())
|
||||
return getNoRegister();
|
||||
|
||||
if (!MayOverwrite && ClobbersBaseRegExceptWriteback(BaseRegIndex))
|
||||
return getNoRegister();
|
||||
|
||||
return Inst.getOperand(BaseRegIndex).getReg();
|
||||
}
|
||||
|
||||
// Store instructions are not handled yet, as they are not important for
|
||||
// pauthtest ABI. Though, they could be handled similar to loads, if needed.
|
||||
|
||||
return getNoRegister();
|
||||
}
|
||||
|
||||
bool isADRP(const MCInst &Inst) const override {
|
||||
return Inst.getOpcode() == AArch64::ADRP;
|
||||
}
|
||||
|
664
bolt/test/binary-analysis/AArch64/gs-pauth-address-checks.s
Normal file
664
bolt/test/binary-analysis/AArch64/gs-pauth-address-checks.s
Normal file
@ -0,0 +1,664 @@
|
||||
// RUN: %clang %cflags -march=armv8.3-a %s -o %t.exe -Wl,--emit-relocs
|
||||
// RUN: llvm-bolt-binary-analysis --scanners=pauth %t.exe 2>&1 | FileCheck %s
|
||||
|
||||
.text
|
||||
|
||||
.globl raise_error
|
||||
.type raise_error,@function
|
||||
raise_error:
|
||||
ret
|
||||
.size raise_error, .-raise_error
|
||||
|
||||
.globl resign_no_check
|
||||
.type resign_no_check,@function
|
||||
resign_no_check:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_no_check, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
autib x0, x1
|
||||
pacia x0, x2
|
||||
ret
|
||||
.size resign_no_check, .-resign_no_check
|
||||
|
||||
// Test "xpac" check method.
|
||||
|
||||
.globl resign_xpaci_good
|
||||
.type resign_xpaci_good,@function
|
||||
resign_xpaci_good:
|
||||
// CHECK-NOT: resign_xpaci_good
|
||||
autib x0, x1
|
||||
mov x16, x0
|
||||
xpaci x16
|
||||
cmp x0, x16
|
||||
b.eq 1f
|
||||
brk 0x1234
|
||||
1:
|
||||
pacia x0, x2
|
||||
ret
|
||||
.size resign_xpaci_good, .-resign_xpaci_good
|
||||
|
||||
.globl resign_xpacd_good
|
||||
.type resign_xpacd_good,@function
|
||||
resign_xpacd_good:
|
||||
// CHECK-NOT: resign_xpacd_good
|
||||
autdb x0, x1
|
||||
mov x16, x0
|
||||
xpacd x16
|
||||
cmp x0, x16
|
||||
b.eq 1f
|
||||
brk 0x1234
|
||||
1:
|
||||
pacda x0, x2
|
||||
ret
|
||||
.size resign_xpacd_good, .-resign_xpacd_good
|
||||
|
||||
.globl resign_xpaci_wrong_error
|
||||
.type resign_xpaci_wrong_error,@function
|
||||
resign_xpaci_wrong_error:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_xpaci_wrong_error, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl raise_error
|
||||
paciasp
|
||||
stp x29, x30, [sp, #-16]!
|
||||
|
||||
autib x0, x1
|
||||
mov x16, x0
|
||||
xpaci x16
|
||||
cmp x0, x16
|
||||
b.eq 1f
|
||||
bl raise_error // should trigger breakpoint instead
|
||||
1:
|
||||
pacia x0, x2
|
||||
|
||||
ldp x29, x30, [sp], #16
|
||||
autiasp
|
||||
ret
|
||||
.size resign_xpaci_wrong_error, .-resign_xpaci_wrong_error
|
||||
|
||||
.globl resign_xpaci_missing_brk
|
||||
.type resign_xpaci_missing_brk,@function
|
||||
resign_xpaci_missing_brk:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_xpaci_missing_brk, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
autib x0, x1
|
||||
mov x16, x0
|
||||
xpaci x16
|
||||
cmp x0, x16
|
||||
b.eq 1f
|
||||
1:
|
||||
pacia x0, x2
|
||||
ret
|
||||
.size resign_xpaci_missing_brk, .-resign_xpaci_missing_brk
|
||||
|
||||
.globl resign_xpaci_missing_branch_and_brk
|
||||
.type resign_xpaci_missing_branch_and_brk,@function
|
||||
resign_xpaci_missing_branch_and_brk:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_xpaci_missing_branch_and_brk, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
autib x0, x1
|
||||
mov x16, x0
|
||||
xpaci x16
|
||||
cmp x0, x16
|
||||
pacia x0, x2
|
||||
ret
|
||||
.size resign_xpaci_missing_branch_and_brk, .-resign_xpaci_missing_branch_and_brk
|
||||
|
||||
.globl resign_xpaci_unrelated_auth_and_check
|
||||
.type resign_xpaci_unrelated_auth_and_check,@function
|
||||
resign_xpaci_unrelated_auth_and_check:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_xpaci_unrelated_auth_and_check, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x10, x2
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
autib x10, x1 // made x10 safe-to-dereference
|
||||
mov x16, x0 // start of checker sequence for x0
|
||||
xpaci x16
|
||||
cmp x0, x16
|
||||
b.eq 1f
|
||||
brk 0x1234
|
||||
1:
|
||||
pacia x10, x2
|
||||
ret
|
||||
.size resign_xpaci_unrelated_auth_and_check, .-resign_xpaci_unrelated_auth_and_check
|
||||
|
||||
// There are lots of operands to check in the pattern - let's at the very least
|
||||
// check that each of the three instructions (mov, xpac, cmp) undergoes *some*
|
||||
// matching. Pay a bit more attention to those instructions and their operands
|
||||
// that can be obviously replaced without crashing at run-time and making the
|
||||
// check obviously weaker.
|
||||
.globl resign_xpaci_wrong_pattern_1
|
||||
.type resign_xpaci_wrong_pattern_1,@function
|
||||
resign_xpaci_wrong_pattern_1:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_xpaci_wrong_pattern_1, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
autib x0, x1
|
||||
mov x16, x10 // x10 instead of x0
|
||||
xpaci x16
|
||||
cmp x0, x16
|
||||
b.eq 1f
|
||||
brk 0x1234
|
||||
1:
|
||||
pacia x0, x2
|
||||
ret
|
||||
.size resign_xpaci_wrong_pattern_1, .-resign_xpaci_wrong_pattern_1
|
||||
|
||||
.globl resign_xpaci_wrong_pattern_2
|
||||
.type resign_xpaci_wrong_pattern_2,@function
|
||||
resign_xpaci_wrong_pattern_2:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_xpaci_wrong_pattern_2, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: xpaci x0
|
||||
autib x0, x1
|
||||
mov x16, x0
|
||||
xpaci x0 // x0 instead of x16
|
||||
cmp x0, x16
|
||||
b.eq 1f
|
||||
brk 0x1234
|
||||
1:
|
||||
pacia x0, x2
|
||||
ret
|
||||
.size resign_xpaci_wrong_pattern_2, .-resign_xpaci_wrong_pattern_2
|
||||
|
||||
.globl resign_xpaci_wrong_pattern_3
|
||||
.type resign_xpaci_wrong_pattern_3,@function
|
||||
resign_xpaci_wrong_pattern_3:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_xpaci_wrong_pattern_3, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
autib x0, x1
|
||||
mov x16, x0
|
||||
xpaci x16
|
||||
cmp x16, x16 // x16 instead of x0
|
||||
b.eq 1f
|
||||
brk 0x1234
|
||||
1:
|
||||
pacia x0, x2
|
||||
ret
|
||||
.size resign_xpaci_wrong_pattern_3, .-resign_xpaci_wrong_pattern_3
|
||||
|
||||
.globl resign_xpaci_wrong_pattern_4
|
||||
.type resign_xpaci_wrong_pattern_4,@function
|
||||
resign_xpaci_wrong_pattern_4:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_xpaci_wrong_pattern_4, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
autib x0, x1
|
||||
mov x16, x0
|
||||
xpaci x16
|
||||
cmp x0, x0 // x0 instead of x16
|
||||
b.eq 1f
|
||||
brk 0x1234
|
||||
1:
|
||||
pacia x0, x2
|
||||
ret
|
||||
.size resign_xpaci_wrong_pattern_4, .-resign_xpaci_wrong_pattern_4
|
||||
|
||||
.globl resign_xpaci_wrong_pattern_5
|
||||
.type resign_xpaci_wrong_pattern_5,@function
|
||||
resign_xpaci_wrong_pattern_5:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_xpaci_wrong_pattern_5, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
autib x0, x1
|
||||
mov x16, x0
|
||||
mov x16, x16 // replace xpaci with a no-op instruction
|
||||
cmp x0, x16
|
||||
b.eq 1f
|
||||
brk 0x1234
|
||||
1:
|
||||
pacia x0, x2
|
||||
ret
|
||||
.size resign_xpaci_wrong_pattern_5, .-resign_xpaci_wrong_pattern_5
|
||||
|
||||
// Test "xpac-hint" check method.
|
||||
|
||||
.globl resign_xpaclri_good
|
||||
.type resign_xpaclri_good,@function
|
||||
resign_xpaclri_good:
|
||||
// CHECK-NOT: resign_xpaclri_good
|
||||
paciasp
|
||||
stp x29, x30, [sp, #-16]!
|
||||
|
||||
autib x30, x1
|
||||
mov x16, x30
|
||||
xpaclri
|
||||
cmp x30, x16
|
||||
b.eq 1f
|
||||
brk 0x1234
|
||||
1:
|
||||
pacia x30, x2
|
||||
|
||||
ldp x29, x30, [sp], #16
|
||||
autiasp
|
||||
ret
|
||||
.size resign_xpaclri_good, .-resign_xpaclri_good
|
||||
|
||||
.globl xpaclri_check_keeps_lr_safe
|
||||
.type xpaclri_check_keeps_lr_safe,@function
|
||||
xpaclri_check_keeps_lr_safe:
|
||||
// CHECK-NOT: xpaclri_check_keeps_lr_safe
|
||||
// LR is implicitly safe-to-dereference and trusted here
|
||||
mov x16, x30
|
||||
xpaclri // clobbers LR
|
||||
cmp x30, x16
|
||||
b.eq 1f
|
||||
brk 0x1234 // marks LR as trusted and safe-to-dereference
|
||||
1:
|
||||
ret // not reporting non-protected return
|
||||
.size xpaclri_check_keeps_lr_safe, .-xpaclri_check_keeps_lr_safe
|
||||
|
||||
.globl xpaclri_check_requires_safe_lr
|
||||
.type xpaclri_check_requires_safe_lr,@function
|
||||
xpaclri_check_requires_safe_lr:
|
||||
// CHECK-LABEL: GS-PAUTH: non-protected ret found in function xpaclri_check_requires_safe_lr, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: ret
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: xpaclri
|
||||
mov x30, x0
|
||||
// LR is not safe-to-dereference here - check that xpac-hint checker
|
||||
// does not make LR safe-to-dereference, but only *keeps* this state.
|
||||
mov x16, x30
|
||||
xpaclri
|
||||
cmp x30, x16
|
||||
b.eq 1f
|
||||
brk 0x1234
|
||||
1:
|
||||
ret
|
||||
.size xpaclri_check_requires_safe_lr, .-xpaclri_check_requires_safe_lr
|
||||
|
||||
.globl resign_xpaclri_wrong_reg
|
||||
.type resign_xpaclri_wrong_reg,@function
|
||||
resign_xpaclri_wrong_reg:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_xpaclri_wrong_reg, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x20, x2
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
paciasp
|
||||
|
||||
autib x20, x1 // consistently using x20 instead of x30
|
||||
mov x16, x20
|
||||
xpaclri // ... but xpaclri still operates on x30
|
||||
cmp x20, x16
|
||||
b.eq 1f
|
||||
brk 0x1234
|
||||
1:
|
||||
pacia x20, x2
|
||||
|
||||
autiasp
|
||||
ret
|
||||
.size resign_xpaclri_wrong_reg, .-resign_xpaclri_wrong_reg
|
||||
|
||||
// Test that pointer should be authenticated AND checked to be safe-to-sign.
|
||||
// Checking alone is not enough.
|
||||
.globl resign_checked_not_authenticated
|
||||
.type resign_checked_not_authenticated,@function
|
||||
resign_checked_not_authenticated:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_checked_not_authenticated, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
mov x16, x0
|
||||
xpaci x16
|
||||
cmp x0, x16
|
||||
b.eq 1f
|
||||
brk 0x1234
|
||||
1:
|
||||
pacia x0, x2
|
||||
ret
|
||||
.size resign_checked_not_authenticated, .-resign_checked_not_authenticated
|
||||
|
||||
// The particular register should be *first* written by an authentication
|
||||
// instruction and *then* that new value should be checked.
|
||||
// Such code pattern will probably crash at run-time anyway, but let's check
|
||||
// "safe-to-dereference" -> "trusted" transition.
|
||||
.globl resign_checked_before_authenticated
|
||||
.type resign_checked_before_authenticated,@function
|
||||
resign_checked_before_authenticated:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_checked_before_authenticated, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
mov x16, x0
|
||||
xpaci x16
|
||||
cmp x0, x16
|
||||
b.eq 1f
|
||||
brk 0x1234
|
||||
1:
|
||||
autib x0, x1
|
||||
pacia x0, x2
|
||||
ret
|
||||
.size resign_checked_before_authenticated, .-resign_checked_before_authenticated
|
||||
|
||||
// Test "high-bits-notbi" check method.
|
||||
|
||||
.globl resign_high_bits_tbz_good
|
||||
.type resign_high_bits_tbz_good,@function
|
||||
resign_high_bits_tbz_good:
|
||||
// CHECK-NOT: resign_high_bits_tbz_good
|
||||
autib x0, x1
|
||||
eor x16, x0, x0, lsl #1
|
||||
tbz x16, #62, 1f
|
||||
brk 0x1234
|
||||
1:
|
||||
pacia x0, x2
|
||||
ret
|
||||
.size resign_high_bits_tbz_good, .-resign_high_bits_tbz_good
|
||||
|
||||
// Check BRK matching briefly - this logic is shared with the "xpac" sequence matcher.
|
||||
|
||||
.globl resign_high_bits_tbz_wrong_error
|
||||
.type resign_high_bits_tbz_wrong_error,@function
|
||||
resign_high_bits_tbz_wrong_error:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_high_bits_tbz_wrong_error, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl raise_error
|
||||
paciasp
|
||||
stp x29, x30, [sp, #-16]!
|
||||
|
||||
autib x0, x1
|
||||
eor x16, x0, x0, lsl #1
|
||||
tbz x16, #62, 1f
|
||||
bl raise_error // should trigger breakpoint instead
|
||||
1:
|
||||
pacia x0, x2
|
||||
|
||||
ldp x29, x30, [sp], #16
|
||||
autiasp
|
||||
ret
|
||||
.size resign_high_bits_tbz_wrong_error, .-resign_high_bits_tbz_wrong_error
|
||||
|
||||
.globl resign_high_bits_tbz_wrong_bit
|
||||
.type resign_high_bits_tbz_wrong_bit,@function
|
||||
resign_high_bits_tbz_wrong_bit:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_high_bits_tbz_wrong_bit, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
autib x0, x1
|
||||
eor x16, x0, x0, lsl #1
|
||||
tbz x16, #63, 1f
|
||||
brk 0x1234
|
||||
1:
|
||||
pacia x0, x2
|
||||
ret
|
||||
.size resign_high_bits_tbz_wrong_bit, .-resign_high_bits_tbz_wrong_bit
|
||||
|
||||
.globl resign_high_bits_tbz_wrong_shift_amount
|
||||
.type resign_high_bits_tbz_wrong_shift_amount,@function
|
||||
resign_high_bits_tbz_wrong_shift_amount:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_high_bits_tbz_wrong_shift_amount, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
autib x0, x1
|
||||
eor x16, x0, x0, lsl #2
|
||||
tbz x16, #62, 1f
|
||||
brk 0x1234
|
||||
1:
|
||||
pacia x0, x2
|
||||
ret
|
||||
.size resign_high_bits_tbz_wrong_shift_amount, .-resign_high_bits_tbz_wrong_shift_amount
|
||||
|
||||
.globl resign_high_bits_tbz_wrong_shift_type
|
||||
.type resign_high_bits_tbz_wrong_shift_type,@function
|
||||
resign_high_bits_tbz_wrong_shift_type:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_high_bits_tbz_wrong_shift_type, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
autib x0, x1
|
||||
eor x16, x0, x0, lsr #1
|
||||
tbz x16, #62, 1f
|
||||
brk 0x1234
|
||||
1:
|
||||
pacia x0, x2
|
||||
ret
|
||||
.size resign_high_bits_tbz_wrong_shift_type, .-resign_high_bits_tbz_wrong_shift_type
|
||||
|
||||
.globl resign_high_bits_tbz_wrong_pattern_1
|
||||
.type resign_high_bits_tbz_wrong_pattern_1,@function
|
||||
resign_high_bits_tbz_wrong_pattern_1:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_high_bits_tbz_wrong_pattern_1, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
autib x0, x1
|
||||
eor x16, x0, x0, lsl #1
|
||||
tbz x17, #62, 1f
|
||||
brk 0x1234
|
||||
1:
|
||||
pacia x0, x2
|
||||
ret
|
||||
.size resign_high_bits_tbz_wrong_pattern_1, .-resign_high_bits_tbz_wrong_pattern_1
|
||||
|
||||
.globl resign_high_bits_tbz_wrong_pattern_2
|
||||
.type resign_high_bits_tbz_wrong_pattern_2,@function
|
||||
resign_high_bits_tbz_wrong_pattern_2:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_high_bits_tbz_wrong_pattern_2, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
autib x0, x1
|
||||
eor x16, x10, x0, lsl #1
|
||||
tbz x16, #62, 1f
|
||||
brk 0x1234
|
||||
1:
|
||||
pacia x0, x2
|
||||
ret
|
||||
.size resign_high_bits_tbz_wrong_pattern_2, .-resign_high_bits_tbz_wrong_pattern_2
|
||||
|
||||
.globl resign_high_bits_tbz_wrong_pattern_3
|
||||
.type resign_high_bits_tbz_wrong_pattern_3,@function
|
||||
resign_high_bits_tbz_wrong_pattern_3:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_high_bits_tbz_wrong_pattern_3, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
autib x0, x1
|
||||
eor x16, x0, x10, lsl #1
|
||||
tbz x16, #62, 1f
|
||||
brk 0x1234
|
||||
1:
|
||||
pacia x0, x2
|
||||
ret
|
||||
.size resign_high_bits_tbz_wrong_pattern_3, .-resign_high_bits_tbz_wrong_pattern_3
|
||||
|
||||
// Test checking by loading via the authenticated pointer.
|
||||
|
||||
.globl resign_load_good
|
||||
.type resign_load_good,@function
|
||||
resign_load_good:
|
||||
// CHECK-NOT: resign_load_good
|
||||
autdb x0, x1
|
||||
ldr x3, [x0]
|
||||
pacda x0, x2
|
||||
ret
|
||||
.size resign_load_good, .-resign_load_good
|
||||
|
||||
.globl resign_load_wreg_good
|
||||
.type resign_load_wreg_good,@function
|
||||
resign_load_wreg_good:
|
||||
// CHECK-NOT: resign_load_wreg_good
|
||||
autdb x0, x1
|
||||
ldr w3, [x0]
|
||||
pacda x0, x2
|
||||
ret
|
||||
.size resign_load_wreg_good, .-resign_load_wreg_good
|
||||
|
||||
.globl resign_load_byte_good
|
||||
.type resign_load_byte_good,@function
|
||||
resign_load_byte_good:
|
||||
// CHECK-NOT: resign_load_byte_good
|
||||
autdb x0, x1
|
||||
ldrb w3, [x0]
|
||||
pacda x0, x2
|
||||
ret
|
||||
.size resign_load_byte_good, .-resign_load_byte_good
|
||||
|
||||
.globl resign_load_pair_good
|
||||
.type resign_load_pair_good,@function
|
||||
resign_load_pair_good:
|
||||
// CHECK-NOT: resign_load_pair_good
|
||||
autdb x0, x1
|
||||
ldp x3, x4, [x0]
|
||||
pacda x0, x2
|
||||
ret
|
||||
.size resign_load_pair_good, .-resign_load_pair_good
|
||||
|
||||
.globl resign_load_imm_offset_good
|
||||
.type resign_load_imm_offset_good,@function
|
||||
resign_load_imm_offset_good:
|
||||
// CHECK-NOT: resign_load_imm_offset_good
|
||||
autdb x0, x1
|
||||
ldr x3, [x0, #16]
|
||||
pacda x0, x2
|
||||
ret
|
||||
.size resign_load_imm_offset_good, .-resign_load_imm_offset_good
|
||||
|
||||
.globl resign_load_preinc_good
|
||||
.type resign_load_preinc_good,@function
|
||||
resign_load_preinc_good:
|
||||
// CHECK-NOT: resign_load_preinc_good
|
||||
autdb x0, x1
|
||||
ldr x3, [x0, #16]!
|
||||
pacda x0, x2
|
||||
ret
|
||||
.size resign_load_preinc_good, .-resign_load_preinc_good
|
||||
|
||||
.globl resign_load_postinc_good
|
||||
.type resign_load_postinc_good,@function
|
||||
resign_load_postinc_good:
|
||||
// CHECK-NOT: resign_load_postinc_good
|
||||
autdb x0, x1
|
||||
ldr x3, [x0], #16
|
||||
pacda x0, x2
|
||||
ret
|
||||
.size resign_load_postinc_good, .-resign_load_postinc_good
|
||||
|
||||
.globl resign_load_pair_with_ptr_writeback_good
|
||||
.type resign_load_pair_with_ptr_writeback_good,@function
|
||||
resign_load_pair_with_ptr_writeback_good:
|
||||
// CHECK-NOT: resign_load_pair_with_ptr_writeback_good
|
||||
autdb x0, x1
|
||||
ldp x3, x4, [x0, #16]! // three output registers (incl. tied x0 register)
|
||||
pacda x0, x2
|
||||
ret
|
||||
.size resign_load_pair_with_ptr_writeback_good, .-resign_load_pair_with_ptr_writeback_good
|
||||
|
||||
.globl resign_load_overwrite
|
||||
.type resign_load_overwrite,@function
|
||||
resign_load_overwrite:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_load_overwrite, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x2
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x0, [x0]
|
||||
autdb x0, x1
|
||||
ldr x0, [x0]
|
||||
pacda x0, x2
|
||||
ret
|
||||
.size resign_load_overwrite, .-resign_load_overwrite
|
||||
|
||||
.globl resign_load_overwrite_out2
|
||||
.type resign_load_overwrite_out2,@function
|
||||
resign_load_overwrite_out2:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_load_overwrite_out2, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x2
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp x10, x0, [x0]
|
||||
autdb x0, x1
|
||||
ldp x10, x0, [x0]
|
||||
pacda x0, x2
|
||||
ret
|
||||
.size resign_load_overwrite_out2, .-resign_load_overwrite_out2
|
||||
|
||||
.globl resign_load_partial_overwrite
|
||||
.type resign_load_partial_overwrite,@function
|
||||
resign_load_partial_overwrite:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_load_partial_overwrite, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x2
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr w0, [x0]
|
||||
autdb x0, x1
|
||||
ldr w0, [x0]
|
||||
pacda x0, x2
|
||||
ret
|
||||
.size resign_load_partial_overwrite, .-resign_load_partial_overwrite
|
||||
|
||||
.globl resign_load_partial_overwrite_out2
|
||||
.type resign_load_partial_overwrite_out2,@function
|
||||
resign_load_partial_overwrite_out2:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_load_partial_overwrite_out2, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x2
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp w10, w0, [x0]
|
||||
autdb x0, x1
|
||||
ldp w10, w0, [x0]
|
||||
pacda x0, x2
|
||||
ret
|
||||
.size resign_load_partial_overwrite_out2, .-resign_load_partial_overwrite_out2
|
||||
|
||||
// Test that base register + offset register addressing mode is rejected.
|
||||
|
||||
.globl resign_load_reg_plus_reg
|
||||
.type resign_load_reg_plus_reg,@function
|
||||
resign_load_reg_plus_reg:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_load_reg_plus_reg, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x2
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
autdb x0, x1
|
||||
ldr x3, [x0, x4]
|
||||
pacda x0, x2
|
||||
ret
|
||||
.size resign_load_reg_plus_reg, .-resign_load_reg_plus_reg
|
||||
|
||||
.globl resign_load_reg_plus_reg_in2
|
||||
.type resign_load_reg_plus_reg_in2,@function
|
||||
resign_load_reg_plus_reg_in2:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_load_reg_plus_reg_in2, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x2
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
autdb x0, x1
|
||||
ldr x3, [x4, x0]
|
||||
pacda x0, x2
|
||||
ret
|
||||
.size resign_load_reg_plus_reg_in2, .-resign_load_reg_plus_reg_in2
|
||||
|
||||
.globl resign_load_unscaled_good
|
||||
.type resign_load_unscaled_good,@function
|
||||
resign_load_unscaled_good:
|
||||
// CHECK-NOT: resign_load_unscaled_good
|
||||
autdb x0, x1
|
||||
ldurb w3, [x0, #-1]
|
||||
pacda x0, x2
|
||||
ret
|
||||
.size resign_load_unscaled_good, .-resign_load_unscaled_good
|
||||
|
||||
// Any basic block can check at most one register using a multi-instruction
|
||||
// pointer-checking sequence, but it can contain an arbitrary number of single-
|
||||
// instruction pointer checks.
|
||||
|
||||
.globl many_checked_regs
|
||||
.type many_checked_regs,@function
|
||||
many_checked_regs:
|
||||
// CHECK-NOT: many_checked_regs
|
||||
autdzb x0
|
||||
autdzb x1
|
||||
autdzb x2
|
||||
b 1f
|
||||
1:
|
||||
ldr w3, [x0] // single-instruction check
|
||||
ldr w3, [x1] // single-instruction check
|
||||
mov x16, x2 // start of multi-instruction checker sequence
|
||||
xpacd x16 // ...
|
||||
cmp x2, x16 // ...
|
||||
b.eq 2f // end of basic block
|
||||
brk 0x1234
|
||||
2:
|
||||
pacdza x0
|
||||
pacdza x1
|
||||
pacdza x2
|
||||
ret
|
||||
.size many_checked_regs, .-many_checked_regs
|
||||
|
||||
.globl main
|
||||
.type main,@function
|
||||
main:
|
||||
mov x0, 0
|
||||
ret
|
||||
.size main, .-main
|
@ -8,13 +8,6 @@
|
||||
// Note that while "instructions that write to the affected registers"
|
||||
// section of the report is still technically correct, it does not necessarily
|
||||
// mention the instructions that are used incorrectly.
|
||||
//
|
||||
// FIXME: Switch to PAC* instructions instead of indirect tail call for testing
|
||||
// if a register is considered safe when detection of signing oracles is
|
||||
// implemented, as it is more traditional usage of PC-relative constants.
|
||||
// Moreover, using PAC instructions would improve test robustness, as
|
||||
// handling of *calls* can be influenced by what BOLT classifies as a
|
||||
// tail call, for example.
|
||||
|
||||
.text
|
||||
|
||||
@ -29,7 +22,8 @@ sym:
|
||||
good_adr:
|
||||
// CHECK-NOT: good_adr
|
||||
adr x0, sym
|
||||
br x0
|
||||
paciza x0
|
||||
ret
|
||||
.size good_adr, .-good_adr
|
||||
|
||||
.globl good_adrp
|
||||
@ -37,7 +31,8 @@ good_adr:
|
||||
good_adrp:
|
||||
// CHECK-NOT: good_adrp
|
||||
adrp x0, sym
|
||||
br x0
|
||||
paciza x0
|
||||
ret
|
||||
.size good_adrp, .-good_adrp
|
||||
|
||||
.globl good_adrp_add
|
||||
@ -46,7 +41,8 @@ good_adrp_add:
|
||||
// CHECK-NOT: good_adrp_add
|
||||
adrp x0, sym
|
||||
add x0, x0, :lo12:sym
|
||||
br x0
|
||||
paciza x0
|
||||
ret
|
||||
.size good_adrp_add, .-good_adrp_add
|
||||
|
||||
.globl good_adrp_add_with_const_offset
|
||||
@ -56,40 +52,45 @@ good_adrp_add_with_const_offset:
|
||||
adrp x0, sym
|
||||
add x0, x0, :lo12:sym
|
||||
add x0, x0, #8
|
||||
br x0
|
||||
paciza x0
|
||||
ret
|
||||
.size good_adrp_add_with_const_offset, .-good_adrp_add_with_const_offset
|
||||
|
||||
.globl bad_adrp_with_nonconst_offset
|
||||
.type bad_adrp_with_nonconst_offset,@function
|
||||
bad_adrp_with_nonconst_offset:
|
||||
// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_adrp_with_nonconst_offset, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x0 # TAILCALL
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_adrp_with_nonconst_offset, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciza x0
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: add x0, x0, x1
|
||||
// CHECK-NEXT: This happens in the following basic block:
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: adrp x0, #{{.*}}
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, x1
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: br x0 # TAILCALL
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: paciza x0
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ret
|
||||
adrp x0, sym
|
||||
add x0, x0, x1
|
||||
br x0
|
||||
paciza x0
|
||||
ret
|
||||
.size bad_adrp_with_nonconst_offset, .-bad_adrp_with_nonconst_offset
|
||||
|
||||
.globl bad_split_adrp
|
||||
.type bad_split_adrp,@function
|
||||
bad_split_adrp:
|
||||
// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_split_adrp, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x0 # UNKNOWN CONTROL FLOW
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_split_adrp, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciza x0
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: add x0, x0, #0x{{[0-9a-f]+}}
|
||||
// CHECK-NEXT: This happens in the following basic block:
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, #0x{{[0-9a-f]+}}
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: br x0 # UNKNOWN CONTROL FLOW
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: paciza x0
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ret
|
||||
cbz x2, 1f
|
||||
adrp x0, sym
|
||||
1:
|
||||
add x0, x0, :lo12:sym
|
||||
br x0
|
||||
paciza x0
|
||||
ret
|
||||
.size bad_split_adrp, .-bad_split_adrp
|
||||
|
||||
// Materialization of absolute addresses is not handled, as it is not expected
|
||||
@ -98,15 +99,17 @@ bad_split_adrp:
|
||||
.globl bad_immediate_constant
|
||||
.type bad_immediate_constant,@function
|
||||
bad_immediate_constant:
|
||||
// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_immediate_constant, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x0 # TAILCALL
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_immediate_constant, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciza x0
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov x0, #{{.*}}
|
||||
// CHECK-NEXT: This happens in the following basic block:
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: mov x0, #{{.*}}
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: br x0 # TAILCALL
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: paciza x0
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ret
|
||||
movz x0, #1234
|
||||
br x0
|
||||
paciza x0
|
||||
ret
|
||||
.size bad_immediate_constant, .-bad_immediate_constant
|
||||
|
||||
// Any ADR or ADRP instruction followed by any number of increments/decrements
|
||||
@ -118,7 +121,8 @@ good_adr_with_add:
|
||||
// CHECK-NOT: good_adr_with_add
|
||||
adr x0, sym
|
||||
add x0, x0, :lo12:sym
|
||||
br x0
|
||||
paciza x0
|
||||
ret
|
||||
.size good_adr_with_add, .-good_adr_with_add
|
||||
|
||||
.globl good_adrp_with_add_non_consecutive
|
||||
@ -128,7 +132,8 @@ good_adrp_with_add_non_consecutive:
|
||||
adrp x0, sym
|
||||
mul x1, x2, x3
|
||||
add x0, x0, :lo12:sym
|
||||
br x0
|
||||
paciza x0
|
||||
ret
|
||||
.size good_adrp_with_add_non_consecutive, .-good_adrp_with_add_non_consecutive
|
||||
|
||||
.globl good_many_offsets
|
||||
@ -138,7 +143,8 @@ good_many_offsets:
|
||||
adrp x0, sym
|
||||
add x1, x0, #8
|
||||
add x2, x1, :lo12:sym
|
||||
br x2
|
||||
paciza x2
|
||||
ret
|
||||
.size good_many_offsets, .-good_many_offsets
|
||||
|
||||
.globl good_negative_offset
|
||||
@ -147,7 +153,8 @@ good_negative_offset:
|
||||
// CHECK-NOT: good_negative_offset
|
||||
adr x0, sym
|
||||
sub x1, x0, #8
|
||||
br x1
|
||||
paciza x1
|
||||
ret
|
||||
.size good_negative_offset, .-good_negative_offset
|
||||
|
||||
// MOV Xd, Xm (which is an alias of ORR Xd, XZR, Xm) is handled as part of
|
||||
@ -161,45 +168,50 @@ good_mov_reg:
|
||||
adrp x0, sym
|
||||
mov x1, x0
|
||||
orr x2, xzr, x1 // the same as "mov x2, x1"
|
||||
br x2
|
||||
paciza x2
|
||||
ret
|
||||
.size good_mov_reg, .-good_mov_reg
|
||||
|
||||
.globl bad_orr_not_xzr
|
||||
.type bad_orr_not_xzr,@function
|
||||
bad_orr_not_xzr:
|
||||
// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_orr_not_xzr, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x2 # TAILCALL
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_orr_not_xzr, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciza x2
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: orr x2, x1, x0
|
||||
// CHECK-NEXT: This happens in the following basic block:
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: adrp x0, #{{(0x)?[0-9a-f]+}}
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: mov x1, #0
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: orr x2, x1, x0
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: br x2 # TAILCALL
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: paciza x2
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ret
|
||||
adrp x0, sym
|
||||
// The generic case of "orr Xd, Xn, Xm" is not allowed so far,
|
||||
// even if Xn is known to be safe
|
||||
movz x1, #0
|
||||
orr x2, x1, x0
|
||||
br x2
|
||||
paciza x2
|
||||
ret
|
||||
.size bad_orr_not_xzr, .-bad_orr_not_xzr
|
||||
|
||||
.globl bad_orr_not_lsl0
|
||||
.type bad_orr_not_lsl0,@function
|
||||
bad_orr_not_lsl0:
|
||||
// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_orr_not_lsl0, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x2 # TAILCALL
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_orr_not_lsl0, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciza x2
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: orr x2, xzr, x0, lsl #1
|
||||
// CHECK-NEXT: This happens in the following basic block:
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: adrp x0, #{{(0x)?[0-9a-f]+}}
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: orr x2, xzr, x0, lsl #1
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: br x2 # TAILCALL
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: paciza x2
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ret
|
||||
adrp x0, sym
|
||||
// Currently, the only allowed form of "orr" is that used by "mov Xd, Xn" alias.
|
||||
// This can be relaxed in the future.
|
||||
orr x2, xzr, x0, lsl #1
|
||||
br x2
|
||||
paciza x2
|
||||
ret
|
||||
.size bad_orr_not_lsl0, .-bad_orr_not_lsl0
|
||||
|
||||
// Check that the input register operands of `add`/`mov` is correct.
|
||||
@ -207,33 +219,37 @@ bad_orr_not_lsl0:
|
||||
.globl bad_add_input_reg
|
||||
.type bad_add_input_reg,@function
|
||||
bad_add_input_reg:
|
||||
// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_add_input_reg, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x0 # TAILCALL
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_add_input_reg, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciza x0
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: add x0, x1, #0x{{[0-9a-f]+}}
|
||||
// CHECK-NEXT: This happens in the following basic block:
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: adrp x0, #{{(0x)?[0-9a-f]+}}
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: add x0, x1, #0x{{[0-9a-f]+}}
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: br x0 # TAILCALL
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: paciza x0
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ret
|
||||
adrp x0, sym
|
||||
add x0, x1, :lo12:sym
|
||||
br x0
|
||||
paciza x0
|
||||
ret
|
||||
.size bad_add_input_reg, .-bad_add_input_reg
|
||||
|
||||
.globl bad_mov_input_reg
|
||||
.type bad_mov_input_reg,@function
|
||||
bad_mov_input_reg:
|
||||
// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_mov_input_reg, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x0 # TAILCALL
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_mov_input_reg, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciza x0
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov x0, x1
|
||||
// CHECK-NEXT: This happens in the following basic block:
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: adrp x0, #{{(0x)?[0-9a-f]+}}
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: mov x0, x1
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: br x0 # TAILCALL
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: paciza x0
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ret
|
||||
adrp x0, sym
|
||||
mov x0, x1
|
||||
br x0
|
||||
paciza x0
|
||||
ret
|
||||
.size bad_mov_input_reg, .-bad_mov_input_reg
|
||||
|
||||
.globl main
|
||||
|
@ -591,7 +591,9 @@ obscure_indirect_call_arg_nocfg:
|
||||
.globl safe_lr_at_function_entry_nocfg
|
||||
.type safe_lr_at_function_entry_nocfg,@function
|
||||
safe_lr_at_function_entry_nocfg:
|
||||
// CHECK-NOT: safe_lr_at_function_entry_nocfg
|
||||
// Due to state being reset after a label, paciasp is reported as
|
||||
// a signing oracle - this is a known false positive, ignore it.
|
||||
// CHECK-NOT: non-protected call{{.*}}safe_lr_at_function_entry_nocfg
|
||||
cbz x0, 1f
|
||||
ret // LR is safe at the start of the function
|
||||
1:
|
||||
|
@ -51,40 +51,40 @@ simple:
|
||||
// CHECK-NEXT: End of Function "simple"
|
||||
// CHECK-EMPTY:
|
||||
// CHECK-NEXT: Running src register safety analysis...
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( hint #25, src-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( stp x29, x30, [sp, #-0x10]!, src-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( b [[BB1]], src-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( hint #25, src-state<SafeToDerefRegs: LR W30 W30_HI , TrustedRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( stp x29, x30, [sp, #-0x10]!, src-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( b [[BB1]], src-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: DataflowSrcSafetyAnalysis::Confluence(
|
||||
// CHECK-NEXT: State 1: src-state<empty>
|
||||
// CHECK-NEXT: State 2: src-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: merged state: src-state<SafeToDerefRegs: , Insts: >
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( autiza x0, src-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: W0 X0 W0_HI , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( blr x0, src-state<SafeToDerefRegs: W0 X0 W0_HI , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ldp x29, x30, [sp], #0x10, src-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( hint #29, src-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: State 2: src-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: merged state: src-state<SafeToDerefRegs: , TrustedRegs: , Insts: >
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( autiza x0, src-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: W0 X0 W0_HI , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( blr x0, src-state<SafeToDerefRegs: W0 X0 W0_HI , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ldp x29, x30, [sp], #0x10, src-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( hint #29, src-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W30 W30_HI , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state<SafeToDerefRegs: LR W30 W30_HI , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W30 W30_HI , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: DataflowSrcSafetyAnalysis::Confluence(
|
||||
// CHECK-NEXT: State 1: src-state<SafeToDerefRegs: , Insts: >
|
||||
// CHECK-NEXT: State 2: src-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: merged state: src-state<SafeToDerefRegs: , Insts: >
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( autiza x0, src-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: W0 X0 W0_HI , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( blr x0, src-state<SafeToDerefRegs: W0 X0 W0_HI , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ldp x29, x30, [sp], #0x10, src-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( hint #29, src-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: State 1: src-state<SafeToDerefRegs: , TrustedRegs: , Insts: >
|
||||
// CHECK-NEXT: State 2: src-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: merged state: src-state<SafeToDerefRegs: , TrustedRegs: , Insts: >
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( autiza x0, src-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: W0 X0 W0_HI , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( blr x0, src-state<SafeToDerefRegs: W0 X0 W0_HI , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ldp x29, x30, [sp], #0x10, src-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( hint #29, src-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W30 W30_HI , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state<SafeToDerefRegs: LR W30 W30_HI , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W30 W30_HI , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: After src register safety analysis:
|
||||
// CHECK-NEXT: Binary Function "simple" {
|
||||
// CHECK-NEXT: Number : 1
|
||||
@ -94,27 +94,30 @@ simple:
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: [[BB0]] (3 instructions, align : 1)
|
||||
// CHECK-NEXT: Entry Point
|
||||
// CHECK-NEXT: 00000000: paciasp # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: 00000004: stp x29, x30, [sp, #-0x10]! # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: 00000008: b [[BB1]] # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: 00000000: paciasp # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: 00000004: stp x29, x30, [sp, #-0x10]! # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: 00000008: b [[BB1]] # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: Successors: [[BB1]]
|
||||
// CHECK-EMPTY:
|
||||
// CHECK-NEXT: [[BB1]] (5 instructions, align : 1)
|
||||
// CHECK-NEXT: Predecessors: [[BB0]]
|
||||
// CHECK-NEXT: 0000000c: autiza x0 # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: 00000010: blr x0 # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: 00000014: ldp x29, x30, [sp], #0x10 # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: 00000018: autiasp # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: 0000001c: ret # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: 0000000c: autiza x0 # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: 00000010: blr x0 # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: 00000014: ldp x29, x30, [sp], #0x10 # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: 00000018: autiasp # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: 0000001c: ret # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
|
||||
// CHECK-EMPTY:
|
||||
// CHECK-NEXT: DWARF CFI Instructions:
|
||||
// CHECK-NEXT: <empty>
|
||||
// CHECK-NEXT: End of Function "simple"
|
||||
// CHECK-EMPTY:
|
||||
// PAUTH-NEXT: Found call inst: 00000000: blr x0 # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
|
||||
// PAUTH-NEXT: Found sign inst: 00000000: paciasp # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
|
||||
// PAUTH-NEXT: Signed reg: LR
|
||||
// PAUTH-NEXT: TrustedRegs: LR W30 W30_HI
|
||||
// PAUTH-NEXT: Found call inst: 00000000: blr x0 # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
|
||||
// PAUTH-NEXT: Call destination reg: X0
|
||||
// PAUTH-NEXT: SafeToDerefRegs: W0 X0 W0_HI{{[ \t]*$}}
|
||||
// CHECK-NEXT: Found RET inst: 00000000: ret # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: Found RET inst: 00000000: ret # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: RetReg: LR
|
||||
// CHECK-NEXT: Authenticated reg: (none)
|
||||
// CHECK-NEXT: SafeToDerefRegs: LR W30 W30_HI{{[ \t]*$}}
|
||||
@ -129,10 +132,10 @@ clobber:
|
||||
// CHECK-LABEL:Analyzing in function clobber, AllocatorId 1
|
||||
// ...
|
||||
// CHECK: Running src register safety analysis...
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( mov w30, #0x0, src-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: W30_HI , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state<SafeToDerefRegs: W30_HI , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: W30_HI , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( mov w30, #0x0, src-state<SafeToDerefRegs: LR W30 W30_HI , TrustedRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: W30_HI , TrustedRegs: W30_HI , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state<SafeToDerefRegs: W30_HI , TrustedRegs: W30_HI , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: W30_HI , TrustedRegs: W30_HI , Insts: >)
|
||||
// CHECK-NEXT: After src register safety analysis:
|
||||
// CHECK-NEXT: Binary Function "clobber" {
|
||||
// ...
|
||||
@ -141,16 +144,16 @@ clobber:
|
||||
// The above output was printed after first run of analysis
|
||||
|
||||
// CHECK-EMPTY:
|
||||
// CHECK-NEXT: Found RET inst: 00000000: ret # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: Found RET inst: 00000000: ret # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: RetReg: LR
|
||||
// CHECK-NEXT: Authenticated reg: (none)
|
||||
// CHECK-NEXT: SafeToDerefRegs: W30_HI{{[ \t]*$}}
|
||||
// CHECK-EMPTY:
|
||||
// CHECK-NEXT: Running detailed src register safety analysis...
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( mov w30, #0x0, src-state<SafeToDerefRegs: LR W30 W30_HI , Insts: [0]()>)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: W30_HI , Insts: [0](0x{{[0-9a-f]+}} )>)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state<SafeToDerefRegs: W30_HI , Insts: [0](0x{{[0-9a-f]+}} )>)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: W30_HI , Insts: [0](0x{{[0-9a-f]+}} )>)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( mov w30, #0x0, src-state<SafeToDerefRegs: LR W30 W30_HI , TrustedRegs: LR W30 W30_HI , Insts: [0]()>)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: W30_HI , TrustedRegs: W30_HI , Insts: [0](0x{{[0-9a-f]+}} )>)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state<SafeToDerefRegs: W30_HI , TrustedRegs: W30_HI , Insts: [0](0x{{[0-9a-f]+}} )>)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: W30_HI , TrustedRegs: W30_HI , Insts: [0](0x{{[0-9a-f]+}} )>)
|
||||
// CHECK-NEXT: After detailed src register safety analysis:
|
||||
// CHECK-NEXT: Binary Function "clobber" {
|
||||
// ...
|
||||
@ -160,7 +163,7 @@ clobber:
|
||||
// Iterating over the reports and attaching clobbering info:
|
||||
|
||||
// CHECK-EMPTY:
|
||||
// CHECK-NEXT: Attaching clobbering info to: 00000000: ret # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: [0](0x{{[0-9a-f]+}} )>
|
||||
// CHECK-NEXT: Attaching clobbering info to: 00000000: ret # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: [0](0x{{[0-9a-f]+}} )>
|
||||
|
||||
.globl nocfg
|
||||
.type nocfg,@function
|
||||
@ -193,13 +196,13 @@ nocfg:
|
||||
// CHECK-NEXT: End of Function "nocfg"
|
||||
// CHECK-EMPTY:
|
||||
// CHECK-NEXT: Running src register safety analysis...
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( adr x0, __ENTRY_nocfg@0x[[ENTRY_ADDR]], src-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( br x0, src-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( adr x0, __ENTRY_nocfg@0x[[ENTRY_ADDR]], src-state<SafeToDerefRegs: LR W30 W30_HI , TrustedRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , TrustedRegs: LR W0 W30 X0 W0_HI W30_HI , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( br x0, src-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , TrustedRegs: LR W0 W30 X0 W0_HI W30_HI , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , TrustedRegs: LR W0 W30 X0 W0_HI W30_HI , Insts: >)
|
||||
// CHECK-NEXT: Due to label, resetting the state before: 00000000: ret # Offset: 8
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
|
||||
// CHECK-NEXT: After src register safety analysis:
|
||||
// CHECK-NEXT: Binary Function "nocfg" {
|
||||
// CHECK-NEXT: Number : 3
|
||||
@ -208,31 +211,31 @@ nocfg:
|
||||
// CHECK: Secondary Entry Points : __ENTRY_nocfg@0x[[ENTRY_ADDR]]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: .{{[A-Za-z0-9]+}}:
|
||||
// CHECK-NEXT: 00000000: adr x0, __ENTRY_nocfg@0x[[ENTRY_ADDR]] # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: 00000004: br x0 # UNKNOWN CONTROL FLOW # Offset: 4 # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: 00000000: adr x0, __ENTRY_nocfg@0x[[ENTRY_ADDR]] # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: 00000004: br x0 # UNKNOWN CONTROL FLOW # Offset: 4 # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: __ENTRY_nocfg@0x[[ENTRY_ADDR]] (Entry Point):
|
||||
// CHECK-NEXT: .{{[A-Za-z0-9]+}}:
|
||||
// CHECK-NEXT: 00000008: ret # Offset: 8 # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: 00000008: ret # Offset: 8 # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: DWARF CFI Instructions:
|
||||
// CHECK-NEXT: <empty>
|
||||
// CHECK-NEXT: End of Function "nocfg"
|
||||
// CHECK-EMPTY:
|
||||
// PAUTH-NEXT: Found call inst: 00000000: br x0 # UNKNOWN CONTROL FLOW # Offset: 4 # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
|
||||
// PAUTH-NEXT: Found call inst: 00000000: br x0 # UNKNOWN CONTROL FLOW # Offset: 4 # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
|
||||
// PAUTH-NEXT: Call destination reg: X0
|
||||
// PAUTH-NEXT: SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI
|
||||
// CHECK-NEXT: Found RET inst: 00000000: ret # Offset: 8 # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: Found RET inst: 00000000: ret # Offset: 8 # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: RetReg: LR
|
||||
// CHECK-NEXT: Authenticated reg: (none)
|
||||
// CHECK-NEXT: SafeToDerefRegs:
|
||||
// CHECK-EMPTY:
|
||||
// CHECK-NEXT: Running detailed src register safety analysis...
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( adr x0, __ENTRY_nocfg@0x[[ENTRY_ADDR]], src-state<SafeToDerefRegs: LR W30 W30_HI , Insts: [0]()>)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , Insts: [0]()>)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( br x0, src-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , Insts: [0]()>)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , Insts: [0]()>)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( adr x0, __ENTRY_nocfg@0x[[ENTRY_ADDR]], src-state<SafeToDerefRegs: LR W30 W30_HI , TrustedRegs: LR W30 W30_HI , Insts: [0]()>)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , TrustedRegs: LR W0 W30 X0 W0_HI W30_HI , Insts: [0]()>)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( br x0, src-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , TrustedRegs: LR W0 W30 X0 W0_HI W30_HI , Insts: [0]()>)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , TrustedRegs: LR W0 W30 X0 W0_HI W30_HI , Insts: [0]()>)
|
||||
// CHECK-NEXT: Due to label, resetting the state before: 00000000: ret # Offset: 8
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state<SafeToDerefRegs: , Insts: [0]()>)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: , Insts: [0]()>)
|
||||
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state<SafeToDerefRegs: , TrustedRegs: , Insts: [0]()>)
|
||||
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: , TrustedRegs: , Insts: [0]()>)
|
||||
// CHECK-NEXT: After detailed src register safety analysis:
|
||||
// CHECK-NEXT: Binary Function "nocfg" {
|
||||
// CHECK-NEXT: Number : 3
|
||||
@ -240,16 +243,16 @@ nocfg:
|
||||
// CHECK: Secondary Entry Points : __ENTRY_nocfg@0x[[ENTRY_ADDR]]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: .{{[A-Za-z0-9]+}}:
|
||||
// CHECK-NEXT: 00000000: adr x0, __ENTRY_nocfg@0x[[ENTRY_ADDR]] # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: [0]()>
|
||||
// CHECK-NEXT: 00000004: br x0 # UNKNOWN CONTROL FLOW # Offset: 4 # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: [0]()>
|
||||
// CHECK-NEXT: 00000000: adr x0, __ENTRY_nocfg@0x[[ENTRY_ADDR]] # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: [0]()>
|
||||
// CHECK-NEXT: 00000004: br x0 # UNKNOWN CONTROL FLOW # Offset: 4 # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: [0]()>
|
||||
// CHECK-NEXT: __ENTRY_nocfg@0x[[ENTRY_ADDR]] (Entry Point):
|
||||
// CHECK-NEXT: .{{[A-Za-z0-9]+}}:
|
||||
// CHECK-NEXT: 00000008: ret # Offset: 8 # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: [0]()>
|
||||
// CHECK-NEXT: 00000008: ret # Offset: 8 # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: [0]()>
|
||||
// CHECK-NEXT: DWARF CFI Instructions:
|
||||
// CHECK-NEXT: <empty>
|
||||
// CHECK-NEXT: End of Function "nocfg"
|
||||
// CHECK-EMPTY:
|
||||
// CHECK-NEXT: Attaching clobbering info to: 00000000: ret # Offset: 8 # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: [0]()>
|
||||
// CHECK-NEXT: Attaching clobbering info to: 00000000: ret # Offset: 8 # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: [0]()>
|
||||
|
||||
// CHECK-LABEL:Analyzing in function main, AllocatorId 1
|
||||
.globl main
|
||||
|
993
bolt/test/binary-analysis/AArch64/gs-pauth-signing-oracles.s
Normal file
993
bolt/test/binary-analysis/AArch64/gs-pauth-signing-oracles.s
Normal file
@ -0,0 +1,993 @@
|
||||
// RUN: %clang %cflags -march=armv8.3-a+pauth-lr -Wl,--no-relax %s -o %t.exe
|
||||
// RUN: llvm-bolt-binary-analysis --scanners=pacret %t.exe 2>&1 | FileCheck -check-prefix=PACRET %s
|
||||
// RUN: llvm-bolt-binary-analysis --scanners=pauth %t.exe 2>&1 | FileCheck %s
|
||||
|
||||
// The detection of compiler-generated explicit pointer checks is tested in
|
||||
// gs-pauth-address-checks.s, for that reason only test here "dummy-load" and
|
||||
// "high-bits-notbi" checkers, as the shortest examples of checkers that are
|
||||
// detected per-instruction and per-BB.
|
||||
|
||||
// PACRET-NOT: signing oracle found in function
|
||||
|
||||
.text
|
||||
|
||||
.type sym,@function
|
||||
sym:
|
||||
ret
|
||||
.size sym, .-sym
|
||||
|
||||
.globl callee
|
||||
.type callee,@function
|
||||
callee:
|
||||
ret
|
||||
.size callee, .-callee
|
||||
|
||||
// Test transitions between register states: none, safe-to-dereference (s-t-d), trusted:
|
||||
// * trusted right away: safe address materialization
|
||||
// * trusted as checked s-t-d: two variants of checks
|
||||
// * untrusted: s-t-d, but not checked
|
||||
// * untrusted: not s-t-d, but checked
|
||||
// * untrusted: not even s-t-d - from arg and from memory
|
||||
// * untrusted: {subreg clobbered, function called} X {between address materialization and use, between auth and check, between check and use}
|
||||
// * untrusted: first checked then auted, auted then auted, checked then checked
|
||||
|
||||
.globl good_sign_addr_mat
|
||||
.type good_sign_addr_mat,@function
|
||||
good_sign_addr_mat:
|
||||
// CHECK-NOT: good_sign_addr_mat
|
||||
adr x0, sym
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size good_sign_addr_mat, .-good_sign_addr_mat
|
||||
|
||||
.globl good_sign_auted_checked_ldr
|
||||
.type good_sign_auted_checked_ldr,@function
|
||||
good_sign_auted_checked_ldr:
|
||||
// CHECK-NOT: good_sign_auted_checked_ldr
|
||||
autda x0, x2
|
||||
ldr x2, [x0]
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size good_sign_auted_checked_ldr, .-good_sign_auted_checked_ldr
|
||||
|
||||
.globl good_sign_auted_checked_brk
|
||||
.type good_sign_auted_checked_brk,@function
|
||||
good_sign_auted_checked_brk:
|
||||
// CHECK-NOT: good_sign_auted_checked_brk
|
||||
autda x0, x2
|
||||
eor x16, x0, x0, lsl #1
|
||||
tbz x16, #62, 1f
|
||||
brk 0x1234
|
||||
1:
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size good_sign_auted_checked_brk, .-good_sign_auted_checked_brk
|
||||
|
||||
.globl bad_sign_authed_unchecked
|
||||
.type bad_sign_authed_unchecked,@function
|
||||
bad_sign_authed_unchecked:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_sign_authed_unchecked, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
autda x0, x2
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size bad_sign_authed_unchecked, .-bad_sign_authed_unchecked
|
||||
|
||||
.globl bad_sign_checked_not_auted
|
||||
.type bad_sign_checked_not_auted,@function
|
||||
bad_sign_checked_not_auted:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_sign_checked_not_auted, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
ldr x2, [x0]
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size bad_sign_checked_not_auted, .-bad_sign_checked_not_auted
|
||||
|
||||
.globl bad_sign_plain_arg
|
||||
.type bad_sign_plain_arg,@function
|
||||
bad_sign_plain_arg:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_sign_plain_arg, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size bad_sign_plain_arg, .-bad_sign_plain_arg
|
||||
|
||||
.globl bad_sign_plain_mem
|
||||
.type bad_sign_plain_mem,@function
|
||||
bad_sign_plain_mem:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_sign_plain_mem, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x0, [x1]
|
||||
// CHECK-NEXT: This happens in the following basic block:
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ldr x0, [x1]
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ret
|
||||
ldr x0, [x1]
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size bad_sign_plain_mem, .-bad_sign_plain_mem
|
||||
|
||||
.globl bad_clobber_between_addr_mat_and_use
|
||||
.type bad_clobber_between_addr_mat_and_use,@function
|
||||
bad_clobber_between_addr_mat_and_use:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_clobber_between_addr_mat_and_use, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w0, w3
|
||||
// CHECK-NEXT: This happens in the following basic block:
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: adr x0, "sym/1"
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: mov w0, w3
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ret
|
||||
adr x0, sym
|
||||
mov w0, w3
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size bad_clobber_between_addr_mat_and_use, .-bad_clobber_between_addr_mat_and_use
|
||||
|
||||
.globl bad_clobber_between_auted_and_checked
|
||||
.type bad_clobber_between_auted_and_checked,@function
|
||||
bad_clobber_between_auted_and_checked:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_clobber_between_auted_and_checked, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w0, w3
|
||||
// CHECK-NEXT: This happens in the following basic block:
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: autda x0, x2
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: mov w0, w3
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ldr x2, [x0]
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ret
|
||||
autda x0, x2
|
||||
mov w0, w3
|
||||
ldr x2, [x0]
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size bad_clobber_between_auted_and_checked, .-bad_clobber_between_auted_and_checked
|
||||
|
||||
.globl bad_clobber_between_checked_and_used
|
||||
.type bad_clobber_between_checked_and_used,@function
|
||||
bad_clobber_between_checked_and_used:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_clobber_between_checked_and_used, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w0, w3
|
||||
// CHECK-NEXT: This happens in the following basic block:
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: autda x0, x2
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ldr x2, [x0]
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: mov w0, w3
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ret
|
||||
autda x0, x2
|
||||
ldr x2, [x0]
|
||||
mov w0, w3
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size bad_clobber_between_checked_and_used, .-bad_clobber_between_checked_and_used
|
||||
|
||||
.globl bad_call_between_addr_mat_and_use
|
||||
.type bad_call_between_addr_mat_and_use,@function
|
||||
bad_call_between_addr_mat_and_use:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_call_between_addr_mat_and_use, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl callee
|
||||
// CHECK-NEXT: This happens in the following basic block:
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: paciasp
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: stp x29, x30, [sp, #-0x10]!
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: mov x29, sp
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: adr x0, "sym/1"
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: bl callee
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: autiasp
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ret
|
||||
paciasp
|
||||
stp x29, x30, [sp, #-16]!
|
||||
mov x29, sp
|
||||
|
||||
adr x0, sym
|
||||
bl callee
|
||||
pacda x0, x1
|
||||
|
||||
ldp x29, x30, [sp], #16
|
||||
autiasp
|
||||
ret
|
||||
.size bad_call_between_addr_mat_and_use, .-bad_call_between_addr_mat_and_use
|
||||
|
||||
.globl bad_call_between_auted_and_checked
|
||||
.type bad_call_between_auted_and_checked,@function
|
||||
bad_call_between_auted_and_checked:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_call_between_auted_and_checked, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl callee
|
||||
// CHECK-NEXT: This happens in the following basic block:
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: paciasp
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: stp x29, x30, [sp, #-0x10]!
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: mov x29, sp
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: autda x0, x2
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: bl callee
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ldr x2, [x0]
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: autiasp
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ret
|
||||
paciasp
|
||||
stp x29, x30, [sp, #-16]!
|
||||
mov x29, sp
|
||||
|
||||
autda x0, x2
|
||||
bl callee
|
||||
ldr x2, [x0]
|
||||
pacda x0, x1
|
||||
|
||||
ldp x29, x30, [sp], #16
|
||||
autiasp
|
||||
ret
|
||||
.size bad_call_between_auted_and_checked, .-bad_call_between_auted_and_checked
|
||||
|
||||
.globl bad_call_between_checked_and_used
|
||||
.type bad_call_between_checked_and_used,@function
|
||||
bad_call_between_checked_and_used:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_call_between_checked_and_used, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl callee
|
||||
// CHECK-NEXT: This happens in the following basic block:
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: paciasp
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: stp x29, x30, [sp, #-0x10]!
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: mov x29, sp
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: autda x0, x2
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ldr x2, [x0]
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: bl callee
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: autiasp
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ret
|
||||
paciasp
|
||||
stp x29, x30, [sp, #-16]!
|
||||
mov x29, sp
|
||||
|
||||
autda x0, x2
|
||||
ldr x2, [x0]
|
||||
bl callee
|
||||
pacda x0, x1
|
||||
|
||||
ldp x29, x30, [sp], #16
|
||||
autiasp
|
||||
ret
|
||||
.size bad_call_between_checked_and_used, .-bad_call_between_checked_and_used
|
||||
|
||||
.globl bad_transition_check_then_auth
|
||||
.type bad_transition_check_then_auth,@function
|
||||
bad_transition_check_then_auth:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_transition_check_then_auth, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
ldr x2, [x0]
|
||||
autda x0, x2
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size bad_transition_check_then_auth, .-bad_transition_check_then_auth
|
||||
|
||||
.globl bad_transition_auth_then_auth
|
||||
.type bad_transition_auth_then_auth,@function
|
||||
bad_transition_auth_then_auth:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_transition_auth_then_auth, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
autda x0, x2
|
||||
autda x0, x2
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size bad_transition_auth_then_auth, .-bad_transition_auth_then_auth
|
||||
|
||||
.globl bad_transition_check_then_check
|
||||
.type bad_transition_check_then_check,@function
|
||||
bad_transition_check_then_check:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_transition_check_then_check, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
ldr x2, [x0]
|
||||
ldr x2, [x0]
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size bad_transition_check_then_check, .-bad_transition_check_then_check
|
||||
|
||||
// Multi-BB test cases.
|
||||
|
||||
// Test state propagation across multiple basic blocks.
|
||||
// Test transitions between register states: none, safe-to-dereference (s-t-d), trusted:
|
||||
// * trusted right away: safe address materialization
|
||||
// * trusted as checked s-t-d: two variants of checks
|
||||
// * untrusted: s-t-d, but not *always* checked
|
||||
// * untrusted: not *always* s-t-d, but checked
|
||||
// * untrusted: not even s-t-d - from arg and from memory
|
||||
// * untrusted: subreg clobbered - between address materialization and use, between auth and check, between check and use
|
||||
// * trusted in both predecessors but for different reasons
|
||||
// (the one due to address materialization and the other due to s-t-d then checked)
|
||||
// * untrusted: auted in one predecessor, checked in the other
|
||||
|
||||
.globl good_sign_addr_mat_multi_bb
|
||||
.type good_sign_addr_mat_multi_bb,@function
|
||||
good_sign_addr_mat_multi_bb:
|
||||
// CHECK-NOT: good_sign_addr_mat_multi_bb
|
||||
adr x0, sym
|
||||
cbz x3, 1f
|
||||
nop
|
||||
1:
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size good_sign_addr_mat_multi_bb, .-good_sign_addr_mat_multi_bb
|
||||
|
||||
.globl good_sign_auted_checked_ldr_multi_bb
|
||||
.type good_sign_auted_checked_ldr_multi_bb,@function
|
||||
good_sign_auted_checked_ldr_multi_bb:
|
||||
// CHECK-NOT: good_sign_auted_checked_ldr_multi_bb
|
||||
autda x0, x2
|
||||
cbz x3, 1f
|
||||
nop
|
||||
1:
|
||||
ldr x2, [x0]
|
||||
cbz x4, 2f
|
||||
nop
|
||||
2:
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size good_sign_auted_checked_ldr_multi_bb, .-good_sign_auted_checked_ldr_multi_bb
|
||||
|
||||
.globl good_sign_auted_checked_brk_multi_bb
|
||||
.type good_sign_auted_checked_brk_multi_bb,@function
|
||||
good_sign_auted_checked_brk_multi_bb:
|
||||
// CHECK-NOT: good_sign_auted_checked_brk_multi_bb
|
||||
autda x0, x2
|
||||
cbz x3, 1f
|
||||
nop
|
||||
1:
|
||||
eor x16, x0, x0, lsl #1
|
||||
tbz x16, #62, 2f
|
||||
brk 0x1234
|
||||
2:
|
||||
cbz x4, 3f
|
||||
nop
|
||||
3:
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size good_sign_auted_checked_brk_multi_bb, .-good_sign_auted_checked_brk_multi_bb
|
||||
|
||||
.globl bad_sign_authed_unchecked_multi_bb
|
||||
.type bad_sign_authed_unchecked_multi_bb,@function
|
||||
bad_sign_authed_unchecked_multi_bb:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_sign_authed_unchecked_multi_bb, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
autda x0, x2
|
||||
cbz x3, 1f
|
||||
ldr x2, [x0]
|
||||
1:
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size bad_sign_authed_unchecked_multi_bb, .-bad_sign_authed_unchecked_multi_bb
|
||||
|
||||
.globl bad_sign_checked_not_auted_multi_bb
|
||||
.type bad_sign_checked_not_auted_multi_bb,@function
|
||||
bad_sign_checked_not_auted_multi_bb:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_sign_checked_not_auted_multi_bb, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
cbz x3, 1f
|
||||
autda x0, x2
|
||||
1:
|
||||
ldr x2, [x0]
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size bad_sign_checked_not_auted_multi_bb, .-bad_sign_checked_not_auted_multi_bb
|
||||
|
||||
.globl bad_sign_plain_arg_multi_bb
|
||||
.type bad_sign_plain_arg_multi_bb,@function
|
||||
bad_sign_plain_arg_multi_bb:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_sign_plain_arg_multi_bb, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
cbz x3, 1f
|
||||
autda x0, x2
|
||||
ldr x2, [x0]
|
||||
1:
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size bad_sign_plain_arg_multi_bb, .-bad_sign_plain_arg_multi_bb
|
||||
|
||||
.globl bad_sign_plain_mem_multi_bb
|
||||
.type bad_sign_plain_mem_multi_bb,@function
|
||||
bad_sign_plain_mem_multi_bb:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_sign_plain_mem_multi_bb, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x0, [x1]
|
||||
ldr x0, [x1]
|
||||
cbz x3, 1f
|
||||
autda x0, x2
|
||||
ldr x2, [x0]
|
||||
1:
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size bad_sign_plain_mem_multi_bb, .-bad_sign_plain_mem_multi_bb
|
||||
|
||||
.globl bad_clobber_between_addr_mat_and_use_multi_bb
|
||||
.type bad_clobber_between_addr_mat_and_use_multi_bb,@function
|
||||
bad_clobber_between_addr_mat_and_use_multi_bb:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_clobber_between_addr_mat_and_use_multi_bb, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w0, w3
|
||||
adr x0, sym
|
||||
cbz x4, 1f
|
||||
mov w0, w3
|
||||
1:
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size bad_clobber_between_addr_mat_and_use_multi_bb, .-bad_clobber_between_addr_mat_and_use_multi_bb
|
||||
|
||||
.globl bad_clobber_between_auted_and_checked_multi_bb
|
||||
.type bad_clobber_between_auted_and_checked_multi_bb,@function
|
||||
bad_clobber_between_auted_and_checked_multi_bb:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_clobber_between_auted_and_checked_multi_bb, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w0, w3
|
||||
autda x0, x2
|
||||
cbz x4, 1f
|
||||
mov w0, w3
|
||||
1:
|
||||
ldr x2, [x0]
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size bad_clobber_between_auted_and_checked_multi_bb, .-bad_clobber_between_auted_and_checked_multi_bb
|
||||
|
||||
.globl bad_clobber_between_checked_and_used_multi_bb
|
||||
.type bad_clobber_between_checked_and_used_multi_bb,@function
|
||||
bad_clobber_between_checked_and_used_multi_bb:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_clobber_between_checked_and_used_multi_bb, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w0, w3
|
||||
autda x0, x2
|
||||
ldr x2, [x0]
|
||||
cbz x4, 1f
|
||||
mov w0, w3
|
||||
1:
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size bad_clobber_between_checked_and_used_multi_bb, .-bad_clobber_between_checked_and_used_multi_bb
|
||||
|
||||
.globl good_both_trusted_multi_bb
|
||||
.type good_both_trusted_multi_bb,@function
|
||||
good_both_trusted_multi_bb:
|
||||
// CHECK-NOT: good_both_trusted_multi_bb
|
||||
cbz x2, 1f
|
||||
autdb x0, x1
|
||||
ldr x2, [x0]
|
||||
b 2f
|
||||
1:
|
||||
adr x0, sym
|
||||
2:
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size good_both_trusted_multi_bb, .-good_both_trusted_multi_bb
|
||||
|
||||
.globl bad_one_auted_one_checked_multi_bb
|
||||
.type bad_one_auted_one_checked_multi_bb,@function
|
||||
bad_one_auted_one_checked_multi_bb:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_one_auted_one_checked_multi_bb, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
cbz x2, 1f
|
||||
autdb x0, x1
|
||||
b 2f
|
||||
1:
|
||||
ldr x3, [x0]
|
||||
2:
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size bad_one_auted_one_checked_multi_bb, .-bad_one_auted_one_checked_multi_bb
|
||||
|
||||
// Test the detection when no CFG was reconstructed for a function.
|
||||
// Test transitions between register states: none, safe-to-dereference (s-t-d), trusted:
|
||||
// * trusted right away: safe address materialization
|
||||
// * trusted as checked s-t-d: only check by load (FIXME: support BRK-based code sequences)
|
||||
// * untrusted: s-t-d, but not checked
|
||||
// * untrusted: not s-t-d, but checked
|
||||
// * untrusted: not even s-t-d - from arg and from memory
|
||||
// * untrusted: subreg clobbered - between address materialization and use, between auth and check, between check and use
|
||||
// * untrusted: first checked then auted, auted then auted, checked then checked
|
||||
//
|
||||
// Note that it is important to sign and authenticate LR, as it is not kept
|
||||
// safe-to-dereference across unconditional branches.
|
||||
|
||||
.globl good_sign_addr_mat_nocfg
|
||||
.type good_sign_addr_mat_nocfg,@function
|
||||
good_sign_addr_mat_nocfg:
|
||||
// CHECK-NOT: good_sign_addr_mat_nocfg
|
||||
paciasp
|
||||
adr x3, 1f
|
||||
br x3
|
||||
1:
|
||||
adr x0, sym
|
||||
pacda x0, x1
|
||||
autiasp
|
||||
ret
|
||||
.size good_sign_addr_mat_nocfg, .-good_sign_addr_mat_nocfg
|
||||
|
||||
.globl good_sign_auted_checked_ldr_nocfg
|
||||
.type good_sign_auted_checked_ldr_nocfg,@function
|
||||
good_sign_auted_checked_ldr_nocfg:
|
||||
// CHECK-NOT: good_sign_auted_checked_ldr_nocfg
|
||||
paciasp
|
||||
adr x3, 1f
|
||||
br x3
|
||||
1:
|
||||
autda x0, x2
|
||||
ldr x2, [x0]
|
||||
pacda x0, x1
|
||||
autiasp
|
||||
ret
|
||||
.size good_sign_auted_checked_ldr_nocfg, .-good_sign_auted_checked_ldr_nocfg
|
||||
|
||||
.globl bad_sign_authed_unchecked_nocfg
|
||||
.type bad_sign_authed_unchecked_nocfg,@function
|
||||
bad_sign_authed_unchecked_nocfg:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_sign_authed_unchecked_nocfg, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
paciasp
|
||||
adr x3, 1f
|
||||
br x3
|
||||
1:
|
||||
autda x0, x2
|
||||
pacda x0, x1
|
||||
autiasp
|
||||
ret
|
||||
.size bad_sign_authed_unchecked_nocfg, .-bad_sign_authed_unchecked_nocfg
|
||||
|
||||
.globl bad_sign_checked_not_auted_nocfg
|
||||
.type bad_sign_checked_not_auted_nocfg,@function
|
||||
bad_sign_checked_not_auted_nocfg:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_sign_checked_not_auted_nocfg, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
paciasp
|
||||
adr x3, 1f
|
||||
br x3
|
||||
1:
|
||||
ldr x2, [x0]
|
||||
pacda x0, x1
|
||||
autiasp
|
||||
ret
|
||||
.size bad_sign_checked_not_auted_nocfg, .-bad_sign_checked_not_auted_nocfg
|
||||
|
||||
.globl bad_sign_plain_arg_nocfg
|
||||
.type bad_sign_plain_arg_nocfg,@function
|
||||
bad_sign_plain_arg_nocfg:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_sign_plain_arg_nocfg, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
paciasp
|
||||
adr x3, 1f
|
||||
br x3
|
||||
1:
|
||||
pacda x0, x1
|
||||
autiasp
|
||||
ret
|
||||
.size bad_sign_plain_arg_nocfg, .-bad_sign_plain_arg_nocfg
|
||||
|
||||
.globl bad_sign_plain_mem_nocfg
|
||||
.type bad_sign_plain_mem_nocfg,@function
|
||||
bad_sign_plain_mem_nocfg:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_sign_plain_mem_nocfg, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x0, [x1]
|
||||
paciasp
|
||||
adr x3, 1f
|
||||
br x3
|
||||
1:
|
||||
ldr x0, [x1]
|
||||
pacda x0, x1
|
||||
autiasp
|
||||
ret
|
||||
.size bad_sign_plain_mem_nocfg, .-bad_sign_plain_mem_nocfg
|
||||
|
||||
.globl bad_clobber_between_addr_mat_and_use_nocfg
|
||||
.type bad_clobber_between_addr_mat_and_use_nocfg,@function
|
||||
bad_clobber_between_addr_mat_and_use_nocfg:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_clobber_between_addr_mat_and_use_nocfg, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w0, w4
|
||||
paciasp
|
||||
adr x3, 1f
|
||||
br x3
|
||||
1:
|
||||
adr x0, sym
|
||||
mov w0, w4
|
||||
pacda x0, x1
|
||||
autiasp
|
||||
ret
|
||||
.size bad_clobber_between_addr_mat_and_use_nocfg, .-bad_clobber_between_addr_mat_and_use_nocfg
|
||||
|
||||
.globl bad_clobber_between_auted_and_checked_nocfg
|
||||
.type bad_clobber_between_auted_and_checked_nocfg,@function
|
||||
bad_clobber_between_auted_and_checked_nocfg:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_clobber_between_auted_and_checked_nocfg, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w0, w4
|
||||
paciasp
|
||||
adr x3, 1f
|
||||
br x3
|
||||
1:
|
||||
autda x0, x2
|
||||
mov w0, w4
|
||||
ldr x2, [x0]
|
||||
pacda x0, x1
|
||||
autiasp
|
||||
ret
|
||||
.size bad_clobber_between_auted_and_checked_nocfg, .-bad_clobber_between_auted_and_checked_nocfg
|
||||
|
||||
.globl bad_clobber_between_checked_and_used_nocfg
|
||||
.type bad_clobber_between_checked_and_used_nocfg,@function
|
||||
bad_clobber_between_checked_and_used_nocfg:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_clobber_between_checked_and_used_nocfg, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w0, w4
|
||||
paciasp
|
||||
adr x3, 1f
|
||||
br x3
|
||||
1:
|
||||
autda x0, x2
|
||||
ldr x2, [x0]
|
||||
mov w0, w4
|
||||
pacda x0, x1
|
||||
autiasp
|
||||
ret
|
||||
.size bad_clobber_between_checked_and_used_nocfg, .-bad_clobber_between_checked_and_used_nocfg
|
||||
|
||||
.globl bad_transition_check_then_auth_nocfg
|
||||
.type bad_transition_check_then_auth_nocfg,@function
|
||||
bad_transition_check_then_auth_nocfg:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_transition_check_then_auth_nocfg, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
paciasp
|
||||
adr x3, 1f
|
||||
br x3
|
||||
1:
|
||||
ldr x2, [x0]
|
||||
autda x0, x2
|
||||
pacda x0, x1
|
||||
autiasp
|
||||
ret
|
||||
.size bad_transition_check_then_auth_nocfg, .-bad_transition_check_then_auth_nocfg
|
||||
|
||||
.globl bad_transition_auth_then_auth_nocfg
|
||||
.type bad_transition_auth_then_auth_nocfg,@function
|
||||
bad_transition_auth_then_auth_nocfg:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_transition_auth_then_auth_nocfg, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
paciasp
|
||||
adr x3, 1f
|
||||
br x3
|
||||
1:
|
||||
autda x0, x2
|
||||
autda x0, x2
|
||||
pacda x0, x1
|
||||
autiasp
|
||||
ret
|
||||
.size bad_transition_auth_then_auth_nocfg, .-bad_transition_auth_then_auth_nocfg
|
||||
|
||||
.globl bad_transition_check_then_check_nocfg
|
||||
.type bad_transition_check_then_check_nocfg,@function
|
||||
bad_transition_check_then_check_nocfg:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_transition_check_then_check_nocfg, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
paciasp
|
||||
adr x3, 1f
|
||||
br x3
|
||||
1:
|
||||
ldr x2, [x0]
|
||||
ldr x2, [x0]
|
||||
pacda x0, x1
|
||||
autiasp
|
||||
ret
|
||||
.size bad_transition_check_then_check_nocfg, .-bad_transition_check_then_check_nocfg
|
||||
|
||||
// Test resign with offset.
|
||||
|
||||
.globl good_resign_with_increment_ldr
|
||||
.type good_resign_with_increment_ldr,@function
|
||||
good_resign_with_increment_ldr:
|
||||
// CHECK-NOT: good_resign_with_increment_ldr
|
||||
autda x0, x2
|
||||
add x0, x0, #8
|
||||
ldr x2, [x0]
|
||||
sub x1, x0, #16
|
||||
mov x2, x1
|
||||
pacda x2, x3
|
||||
ret
|
||||
.size good_resign_with_increment_ldr, .-good_resign_with_increment_ldr
|
||||
|
||||
.globl good_resign_with_increment_brk
|
||||
.type good_resign_with_increment_brk,@function
|
||||
good_resign_with_increment_brk:
|
||||
// CHECK-NOT: good_resign_with_increment_brk
|
||||
autda x0, x2
|
||||
add x0, x0, #8
|
||||
eor x16, x0, x0, lsl #1
|
||||
tbz x16, #62, 1f
|
||||
brk 0x1234
|
||||
1:
|
||||
mov x2, x0
|
||||
pacda x2, x1
|
||||
ret
|
||||
.size good_resign_with_increment_brk, .-good_resign_with_increment_brk
|
||||
|
||||
.globl bad_nonconstant_auth_increment_check
|
||||
.type bad_nonconstant_auth_increment_check,@function
|
||||
bad_nonconstant_auth_increment_check:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_nonconstant_auth_increment_check, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: add x0, x0, x1
|
||||
// CHECK-NEXT: This happens in the following basic block:
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: autda x0, x2
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, x1
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ldr x2, [x0]
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ret
|
||||
autda x0, x2
|
||||
add x0, x0, x1
|
||||
ldr x2, [x0]
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size bad_nonconstant_auth_increment_check, .-bad_nonconstant_auth_increment_check
|
||||
|
||||
.globl bad_nonconstant_auth_check_increment
|
||||
.type bad_nonconstant_auth_check_increment,@function
|
||||
bad_nonconstant_auth_check_increment:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_nonconstant_auth_check_increment, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: add x0, x0, x1
|
||||
// CHECK-NEXT: This happens in the following basic block:
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: autda x0, x2
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ldr x2, [x0]
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, x1
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: pacda x0, x1
|
||||
// CHECK-NEXT: {{[0-9a-f]+}}: ret
|
||||
autda x0, x2
|
||||
ldr x2, [x0]
|
||||
add x0, x0, x1
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size bad_nonconstant_auth_check_increment, .-bad_nonconstant_auth_check_increment
|
||||
|
||||
// Test that all the expected signing instructions are recornized.
|
||||
|
||||
.globl inst_pacda
|
||||
.type inst_pacda,@function
|
||||
inst_pacda:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacda, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1
|
||||
pacda x0, x1
|
||||
ret
|
||||
.size inst_pacda, .-inst_pacda
|
||||
|
||||
.globl inst_pacdza
|
||||
.type inst_pacdza,@function
|
||||
inst_pacdza:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacdza, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacdza x0
|
||||
pacdza x0
|
||||
ret
|
||||
.size inst_pacdza, .-inst_pacdza
|
||||
|
||||
.globl inst_pacdb
|
||||
.type inst_pacdb,@function
|
||||
inst_pacdb:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacdb, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacdb x0, x1
|
||||
pacdb x0, x1
|
||||
ret
|
||||
.size inst_pacdb, .-inst_pacdb
|
||||
|
||||
.globl inst_pacdzb
|
||||
.type inst_pacdzb,@function
|
||||
inst_pacdzb:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacdzb, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacdzb x0
|
||||
pacdzb x0
|
||||
ret
|
||||
.size inst_pacdzb, .-inst_pacdzb
|
||||
|
||||
.globl inst_pacia
|
||||
.type inst_pacia,@function
|
||||
inst_pacia:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacia, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x1
|
||||
pacia x0, x1
|
||||
ret
|
||||
.size inst_pacia, .-inst_pacia
|
||||
|
||||
.globl inst_pacia1716
|
||||
.type inst_pacia1716,@function
|
||||
inst_pacia1716:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacia1716, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia1716
|
||||
pacia1716
|
||||
ret
|
||||
.size inst_pacia1716, .-inst_pacia1716
|
||||
|
||||
.globl inst_paciasp
|
||||
.type inst_paciasp,@function
|
||||
inst_paciasp:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_paciasp, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciasp
|
||||
mov x30, x0
|
||||
paciasp // signs LR
|
||||
autiasp
|
||||
ret
|
||||
.size inst_paciasp, .-inst_paciasp
|
||||
|
||||
.globl inst_paciaz
|
||||
.type inst_paciaz,@function
|
||||
inst_paciaz:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_paciaz, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciaz
|
||||
mov x30, x0
|
||||
paciaz // signs LR
|
||||
autiaz
|
||||
ret
|
||||
.size inst_paciaz, .-inst_paciaz
|
||||
|
||||
.globl inst_paciza
|
||||
.type inst_paciza,@function
|
||||
inst_paciza:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_paciza, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciza x0
|
||||
paciza x0
|
||||
ret
|
||||
.size inst_paciza, .-inst_paciza
|
||||
|
||||
.globl inst_pacia171615
|
||||
.type inst_pacia171615,@function
|
||||
inst_pacia171615:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacia171615, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia171615
|
||||
mov x30, x0
|
||||
pacia171615 // signs LR
|
||||
autia171615
|
||||
ret
|
||||
.size inst_pacia171615, .-inst_pacia171615
|
||||
|
||||
.globl inst_paciasppc
|
||||
.type inst_paciasppc,@function
|
||||
inst_paciasppc:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_paciasppc, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciasppc
|
||||
mov x30, x0
|
||||
1:
|
||||
paciasppc // signs LR
|
||||
autiasppc 1b
|
||||
ret
|
||||
.size inst_paciasppc, .-inst_paciasppc
|
||||
|
||||
.globl inst_pacib
|
||||
.type inst_pacib,@function
|
||||
inst_pacib:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacib, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacib x0, x1
|
||||
pacib x0, x1
|
||||
ret
|
||||
.size inst_pacib, .-inst_pacib
|
||||
|
||||
.globl inst_pacib1716
|
||||
.type inst_pacib1716,@function
|
||||
inst_pacib1716:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacib1716, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacib1716
|
||||
pacib1716
|
||||
ret
|
||||
.size inst_pacib1716, .-inst_pacib1716
|
||||
|
||||
.globl inst_pacibsp
|
||||
.type inst_pacibsp,@function
|
||||
inst_pacibsp:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacibsp, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacibsp
|
||||
mov x30, x0
|
||||
pacibsp // signs LR
|
||||
autibsp
|
||||
ret
|
||||
.size inst_pacibsp, .-inst_pacibsp
|
||||
|
||||
.globl inst_pacibz
|
||||
.type inst_pacibz,@function
|
||||
inst_pacibz:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacibz, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacibz
|
||||
mov x30, x0
|
||||
pacibz // signs LR
|
||||
autibz
|
||||
ret
|
||||
.size inst_pacibz, .-inst_pacibz
|
||||
|
||||
.globl inst_pacizb
|
||||
.type inst_pacizb,@function
|
||||
inst_pacizb:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacizb, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacizb x0
|
||||
pacizb x0
|
||||
ret
|
||||
.size inst_pacizb, .-inst_pacizb
|
||||
|
||||
.globl inst_pacib171615
|
||||
.type inst_pacib171615,@function
|
||||
inst_pacib171615:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacib171615, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacib171615
|
||||
mov x30, x0
|
||||
pacib171615 // signs LR
|
||||
autib171615
|
||||
ret
|
||||
.size inst_pacib171615, .-inst_pacib171615
|
||||
|
||||
.globl inst_pacibsppc
|
||||
.type inst_pacibsppc,@function
|
||||
inst_pacibsppc:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacibsppc, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacibsppc
|
||||
mov x30, x0
|
||||
1:
|
||||
pacibsppc // signs LR
|
||||
autibsppc 1b
|
||||
ret
|
||||
.size inst_pacibsppc, .-inst_pacibsppc
|
||||
|
||||
.globl inst_pacnbiasppc
|
||||
.type inst_pacnbiasppc,@function
|
||||
inst_pacnbiasppc:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacnbiasppc, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacnbiasppc
|
||||
mov x30, x0
|
||||
1:
|
||||
pacnbiasppc // signs LR
|
||||
autiasppc 1b
|
||||
ret
|
||||
.size inst_pacnbiasppc, .-inst_pacnbiasppc
|
||||
|
||||
.globl inst_pacnbibsppc
|
||||
.type inst_pacnbibsppc,@function
|
||||
inst_pacnbibsppc:
|
||||
// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacnbibsppc, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacnbibsppc
|
||||
mov x30, x0
|
||||
1:
|
||||
pacnbibsppc // signs LR
|
||||
autibsppc 1b
|
||||
ret
|
||||
.size inst_pacnbibsppc, .-inst_pacnbibsppc
|
||||
|
||||
.globl main
|
||||
.type main,@function
|
||||
main:
|
||||
mov x0, 0
|
||||
ret
|
||||
.size main, .-main
|
Loading…
x
Reference in New Issue
Block a user