[BOLT] Gadget scanner: reformulate the state for data-flow analysis (#131898)
In preparation for implementing support for detection of non-protected call instructions, refine the definition of state which is computed for each register by data-flow analysis. Explicitly marking the registers which are known to be trusted at function entry is crucial for finding non-protected calls. In addition, it fixes less-common false negatives for pac-ret, such as `ret x1` in `f_nonx30_ret_non_auted` test case.
This commit is contained in:
parent
f7f5aa217a
commit
b6b40e9ac9
@ -551,6 +551,16 @@ public:
|
||||
return Analysis->isReturn(Inst);
|
||||
}
|
||||
|
||||
/// Returns the registers that are trusted at function entry.
|
||||
///
|
||||
/// Each register should be treated as if a successfully authenticated
|
||||
/// pointer was written to it before entering the function (i.e. the
|
||||
/// pointer is safe to jump to as well as to be signed).
|
||||
virtual SmallVector<MCPhysReg> getTrustedLiveInRegs() const {
|
||||
llvm_unreachable("not implemented");
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual ErrorOr<MCPhysReg> getAuthenticatedReg(const MCInst &Inst) const {
|
||||
llvm_unreachable("not implemented");
|
||||
return getNoRegister();
|
||||
|
@ -212,7 +212,7 @@ struct GadgetReport : public Report {
|
||||
// The particular kind of gadget that is detected.
|
||||
const GadgetKind &Kind;
|
||||
// The set of registers related to this gadget report (possibly empty).
|
||||
SmallVector<MCPhysReg> AffectedRegisters;
|
||||
SmallVector<MCPhysReg, 1> AffectedRegisters;
|
||||
// The instructions that clobber the affected registers.
|
||||
// There is no one-to-one correspondence with AffectedRegisters: for example,
|
||||
// the same register can be overwritten by different instructions in different
|
||||
@ -220,9 +220,8 @@ struct GadgetReport : public Report {
|
||||
SmallVector<MCInstReference> OverwritingInstrs;
|
||||
|
||||
GadgetReport(const GadgetKind &Kind, MCInstReference Location,
|
||||
const BitVector &AffectedRegisters)
|
||||
: Report(Location), Kind(Kind),
|
||||
AffectedRegisters(AffectedRegisters.set_bits()) {}
|
||||
MCPhysReg AffectedRegister)
|
||||
: Report(Location), Kind(Kind), AffectedRegisters({AffectedRegister}) {}
|
||||
|
||||
void generateReport(raw_ostream &OS, const BinaryContext &BC) const override;
|
||||
|
||||
|
@ -126,18 +126,16 @@ public:
|
||||
|
||||
// The security property that is checked is:
|
||||
// When a register is used as the address to jump to in a return instruction,
|
||||
// that register must either:
|
||||
// (a) never be changed within this function, i.e. have the same value as when
|
||||
// the function started, or
|
||||
// that register must be safe-to-dereference. It must either
|
||||
// (a) be safe-to-dereference at function entry and never be changed within this
|
||||
// function, i.e. have the same value as when the function started, or
|
||||
// (b) the last write to the register must be by an authentication instruction.
|
||||
|
||||
// This property is checked by using dataflow analysis to keep track of which
|
||||
// registers have been written (def-ed), since last authenticated. Those are
|
||||
// exactly the registers containing values that should not be trusted (as they
|
||||
// could have changed since the last time they were authenticated). For pac-ret,
|
||||
// any return instruction using such a register is a gadget to be reported. For
|
||||
// PAuthABI, probably at least any indirect control flow using such a register
|
||||
// should be reported.
|
||||
// registers have been written (def-ed), since last authenticated. For pac-ret,
|
||||
// any return instruction using a register which is not safe-to-dereference is
|
||||
// a gadget to be reported. For PAuthABI, probably at least any indirect control
|
||||
// flow using such a register should be reported.
|
||||
|
||||
// Furthermore, when producing a diagnostic for a found non-pac-ret protected
|
||||
// return, the analysis also lists the last instructions that wrote to the
|
||||
@ -156,10 +154,29 @@ public:
|
||||
// in the gadgets to be reported. This information is used in the second run
|
||||
// to also track which instructions last wrote to those registers.
|
||||
|
||||
/// A state representing which registers are safe to use by an instruction
|
||||
/// at a given program point.
|
||||
///
|
||||
/// To simplify reasoning, let's stick with the following approach:
|
||||
/// * when state is updated by the data-flow analysis, the sub-, super- and
|
||||
/// overlapping registers are marked as needed
|
||||
/// * when the particular instruction is checked if it represents a gadget,
|
||||
/// the specific bit of BitVector should be usable to answer this.
|
||||
///
|
||||
/// For example, on AArch64:
|
||||
/// * An AUTIZA X0 instruction marks both X0 and W0 (as well as W0_HI) as
|
||||
/// safe-to-dereference. It does not change the state of X0_X1, for example,
|
||||
/// as super-registers partially retain their old, unsafe values.
|
||||
/// * LDR X1, [X0] marks as unsafe both X1 itself and anything it overlaps
|
||||
/// with: W1, W1_HI, X0_X1 and so on.
|
||||
/// * RET (which is implicitly RET X30) is a protected return if and only if
|
||||
/// X30 is safe-to-dereference - the state computed for sub- and
|
||||
/// super-registers is not inspected.
|
||||
struct State {
|
||||
/// A BitVector containing the registers that have been clobbered, and
|
||||
/// not authenticated.
|
||||
BitVector NonAutClobRegs;
|
||||
/// A BitVector containing the registers that are either safe at function
|
||||
/// entry and were not clobbered yet, or those not clobbered since being
|
||||
/// authenticated.
|
||||
BitVector SafeToDerefRegs;
|
||||
/// 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
|
||||
@ -167,18 +184,32 @@ struct State {
|
||||
/// only use register `X30`, and therefore, this vector will probably have
|
||||
/// length 1 in the second run.
|
||||
std::vector<SmallPtrSet<const MCInst *, 4>> LastInstWritingReg;
|
||||
|
||||
/// Construct an empty state.
|
||||
State() {}
|
||||
|
||||
State(unsigned NumRegs, unsigned NumRegsToTrack)
|
||||
: NonAutClobRegs(NumRegs), LastInstWritingReg(NumRegsToTrack) {}
|
||||
State &operator|=(const State &StateIn) {
|
||||
NonAutClobRegs |= StateIn.NonAutClobRegs;
|
||||
: SafeToDerefRegs(NumRegs), LastInstWritingReg(NumRegsToTrack) {}
|
||||
|
||||
State &merge(const State &StateIn) {
|
||||
if (StateIn.empty())
|
||||
return *this;
|
||||
if (empty())
|
||||
return (*this = StateIn);
|
||||
|
||||
SafeToDerefRegs &= StateIn.SafeToDerefRegs;
|
||||
for (unsigned I = 0; I < LastInstWritingReg.size(); ++I)
|
||||
for (const MCInst *J : StateIn.LastInstWritingReg[I])
|
||||
LastInstWritingReg[I].insert(J);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Returns true if this object does not store state of any registers -
|
||||
/// neither safe, nor unsafe ones.
|
||||
bool empty() const { return SafeToDerefRegs.empty(); }
|
||||
|
||||
bool operator==(const State &RHS) const {
|
||||
return NonAutClobRegs == RHS.NonAutClobRegs &&
|
||||
return SafeToDerefRegs == RHS.SafeToDerefRegs &&
|
||||
LastInstWritingReg == RHS.LastInstWritingReg;
|
||||
}
|
||||
bool operator!=(const State &RHS) const { return !((*this) == RHS); }
|
||||
@ -199,8 +230,12 @@ static void printLastInsts(
|
||||
|
||||
raw_ostream &operator<<(raw_ostream &OS, const State &S) {
|
||||
OS << "pacret-state<";
|
||||
OS << "NonAutClobRegs: " << S.NonAutClobRegs << ", ";
|
||||
printLastInsts(OS, S.LastInstWritingReg);
|
||||
if (S.empty()) {
|
||||
OS << "empty";
|
||||
} else {
|
||||
OS << "SafeToDerefRegs: " << S.SafeToDerefRegs << ", ";
|
||||
printLastInsts(OS, S.LastInstWritingReg);
|
||||
}
|
||||
OS << ">";
|
||||
return OS;
|
||||
}
|
||||
@ -217,10 +252,16 @@ private:
|
||||
void PacStatePrinter::print(raw_ostream &OS, const State &S) const {
|
||||
RegStatePrinter RegStatePrinter(BC);
|
||||
OS << "pacret-state<";
|
||||
OS << "NonAutClobRegs: ";
|
||||
RegStatePrinter.print(OS, S.NonAutClobRegs);
|
||||
OS << ", ";
|
||||
printLastInsts(OS, S.LastInstWritingReg);
|
||||
if (S.empty()) {
|
||||
assert(S.SafeToDerefRegs.empty());
|
||||
assert(S.LastInstWritingReg.empty());
|
||||
OS << "empty";
|
||||
} else {
|
||||
OS << "SafeToDerefRegs: ";
|
||||
RegStatePrinter.print(OS, S.SafeToDerefRegs);
|
||||
OS << ", ";
|
||||
printLastInsts(OS, S.LastInstWritingReg);
|
||||
}
|
||||
OS << ">";
|
||||
}
|
||||
|
||||
@ -257,14 +298,22 @@ protected:
|
||||
|
||||
void preflight() {}
|
||||
|
||||
State getStartingStateAtBB(const BinaryBasicBlock &BB) {
|
||||
return State(NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters());
|
||||
State createEntryState() {
|
||||
State S(NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters());
|
||||
for (MCPhysReg Reg : BC.MIB->getTrustedLiveInRegs())
|
||||
S.SafeToDerefRegs |= BC.MIB->getAliases(Reg, /*OnlySmaller=*/true);
|
||||
return S;
|
||||
}
|
||||
|
||||
State getStartingStateAtPoint(const MCInst &Point) {
|
||||
return State(NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters());
|
||||
State getStartingStateAtBB(const BinaryBasicBlock &BB) {
|
||||
if (BB.isEntryPoint())
|
||||
return createEntryState();
|
||||
|
||||
return State();
|
||||
}
|
||||
|
||||
State getStartingStateAtPoint(const MCInst &Point) { return State(); }
|
||||
|
||||
void doConfluence(State &StateOut, const State &StateIn) {
|
||||
PacStatePrinter P(BC);
|
||||
LLVM_DEBUG({
|
||||
@ -277,7 +326,7 @@ protected:
|
||||
dbgs() << ")\n";
|
||||
});
|
||||
|
||||
StateOut |= StateIn;
|
||||
StateOut.merge(StateIn);
|
||||
|
||||
LLVM_DEBUG({
|
||||
dbgs() << " merged state: ";
|
||||
@ -297,8 +346,17 @@ protected:
|
||||
dbgs() << ")\n";
|
||||
});
|
||||
|
||||
// If this instruction is reachable, a non-empty state will be propagated
|
||||
// to it from the entry basic block sooner or later. Until then, it is both
|
||||
// more efficient and easier to reason about to skip computeNext().
|
||||
if (Cur.empty()) {
|
||||
LLVM_DEBUG(
|
||||
{ dbgs() << "Skipping computeNext(Point, Cur) as Cur is empty.\n"; });
|
||||
return State();
|
||||
}
|
||||
|
||||
State Next = Cur;
|
||||
BitVector Written = BitVector(NumRegs, false);
|
||||
BitVector Clobbered(NumRegs, false);
|
||||
// Assume a call can clobber all registers, including callee-saved
|
||||
// registers. There's a good chance that callee-saved registers will be
|
||||
// saved on the stack at some point during execution of the callee.
|
||||
@ -307,36 +365,27 @@ protected:
|
||||
// Also, not all functions may respect the AAPCS ABI rules about
|
||||
// caller/callee-saved registers.
|
||||
if (BC.MIB->isCall(Point))
|
||||
Written.set();
|
||||
Clobbered.set();
|
||||
else
|
||||
// FIXME: `getWrittenRegs` only sets the register directly written in the
|
||||
// instruction, and the smaller aliasing registers. It does not set the
|
||||
// larger aliasing registers. To also set the larger aliasing registers,
|
||||
// we'd have to call `getClobberedRegs`.
|
||||
// It is unclear if there is any test case which shows a different
|
||||
// behaviour between using `getWrittenRegs` vs `getClobberedRegs`. We'd
|
||||
// first would like to see such a test case before making a decision
|
||||
// on whether using `getClobberedRegs` below would be better.
|
||||
// Also see the discussion on this at
|
||||
// https://github.com/llvm/llvm-project/pull/122304#discussion_r1939511909
|
||||
BC.MIB->getWrittenRegs(Point, Written);
|
||||
Next.NonAutClobRegs |= Written;
|
||||
BC.MIB->getClobberedRegs(Point, Clobbered);
|
||||
Next.SafeToDerefRegs.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())
|
||||
if (Written[Reg])
|
||||
if (Clobbered[Reg])
|
||||
lastWritingInsts(Next, Reg) = {&Point};
|
||||
|
||||
ErrorOr<MCPhysReg> AutReg = BC.MIB->getAuthenticatedReg(Point);
|
||||
if (AutReg && *AutReg != BC.MIB->getNoRegister()) {
|
||||
// FIXME: should we use `OnlySmaller=false` below? See similar
|
||||
// FIXME about `getWrittenRegs` above and further discussion about this
|
||||
// at
|
||||
// https://github.com/llvm/llvm-project/pull/122304#discussion_r1939515516
|
||||
Next.NonAutClobRegs.reset(
|
||||
BC.MIB->getAliases(*AutReg, /*OnlySmaller=*/true));
|
||||
if (RegsToTrackInstsFor.isTracked(*AutReg))
|
||||
lastWritingInsts(Next, *AutReg).clear();
|
||||
// The sub-registers of *AutReg are also trusted now, but not its
|
||||
// super-registers (as they retain untrusted register units).
|
||||
BitVector AuthenticatedSubregs =
|
||||
BC.MIB->getAliases(*AutReg, /*OnlySmaller=*/true);
|
||||
for (MCPhysReg Reg : AuthenticatedSubregs.set_bits()) {
|
||||
Next.SafeToDerefRegs.set(Reg);
|
||||
if (RegsToTrackInstsFor.isTracked(Reg))
|
||||
lastWritingInsts(Next, Reg).clear();
|
||||
}
|
||||
}
|
||||
|
||||
LLVM_DEBUG({
|
||||
@ -397,14 +446,11 @@ shouldReportReturnGadget(const BinaryContext &BC, const MCInstReference &Inst,
|
||||
});
|
||||
if (BC.MIB->isAuthenticationOfReg(Inst, RetReg))
|
||||
return nullptr;
|
||||
BitVector UsedDirtyRegs = S.NonAutClobRegs;
|
||||
LLVM_DEBUG({ traceRegMask(BC, "NonAutClobRegs at Ret", UsedDirtyRegs); });
|
||||
UsedDirtyRegs &= BC.MIB->getAliases(RetReg, /*OnlySmaller=*/true);
|
||||
LLVM_DEBUG({ traceRegMask(BC, "Intersection with RetReg", UsedDirtyRegs); });
|
||||
if (!UsedDirtyRegs.any())
|
||||
LLVM_DEBUG({ traceRegMask(BC, "SafeToDerefRegs", S.SafeToDerefRegs); });
|
||||
if (S.SafeToDerefRegs[RetReg])
|
||||
return nullptr;
|
||||
|
||||
return std::make_shared<GadgetReport>(RetKind, Inst, UsedDirtyRegs);
|
||||
return std::make_shared<GadgetReport>(RetKind, Inst, RetReg);
|
||||
}
|
||||
|
||||
FunctionAnalysisResult
|
||||
@ -425,6 +471,14 @@ Analysis::findGadgets(BinaryFunction &BF,
|
||||
MCInstReference Inst(&BB, I);
|
||||
const State &S = *PRA.getStateAt(Inst);
|
||||
|
||||
// If non-empty state was never propagated from the entry basic block
|
||||
// to Inst, assume it to be unreachable and report a warning.
|
||||
if (S.empty()) {
|
||||
Result.Diagnostics.push_back(std::make_shared<GenericReport>(
|
||||
Inst, "Warning: unreachable instruction found"));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto Report = shouldReportReturnGadget(BC, Inst, S))
|
||||
Result.Diagnostics.push_back(Report);
|
||||
}
|
||||
|
@ -191,6 +191,10 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
SmallVector<MCPhysReg> getTrustedLiveInRegs() const override {
|
||||
return {AArch64::LR};
|
||||
}
|
||||
|
||||
ErrorOr<MCPhysReg> getAuthenticatedReg(const MCInst &Inst) const override {
|
||||
switch (Inst.getOpcode()) {
|
||||
case AArch64::AUTIAZ:
|
||||
|
@ -183,17 +183,14 @@ f_tail_called:
|
||||
.globl f_nonx30_ret_non_auted
|
||||
.type f_nonx30_ret_non_auted,@function
|
||||
f_nonx30_ret_non_auted:
|
||||
// FIXME: x1 is not authenticated, so should this be reported?
|
||||
// Note that we assume it's fine for x30 to not be authenticated before
|
||||
// returning to, as assuming that x30 is not attacker controlled at function
|
||||
// entry is part (implicitly) of the pac-ret hardening scheme.
|
||||
// It's probably an open question whether for other hardening schemes, such as
|
||||
// PAuthABI, which registers should be considered "clean" or not at function entry.
|
||||
// In other words, which registers have to be authenticated before being used as
|
||||
// a pointer and which ones not?
|
||||
// For a more detailed discussion, see
|
||||
// https://github.com/llvm/llvm-project/pull/122304#discussion_r1923662744
|
||||
// CHECK-NOT: f_nonx30_ret_non_auted
|
||||
// x1 is neither authenticated nor implicitly considered safe at function entry.
|
||||
// Note that we assume it's fine for x30 to not be authenticated before
|
||||
// returning to, as assuming that x30 is not attacker controlled at function
|
||||
// entry is part (implicitly) of the pac-ret hardening scheme.
|
||||
//
|
||||
// CHECK-LABEL: GS-PAUTH: non-protected ret found in function f_nonx30_ret_non_auted, basic block {{[0-9a-zA-Z.]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: ret
|
||||
// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
|
||||
ret x1
|
||||
.size f_nonx30_ret_non_auted, .-f_nonx30_ret_non_auted
|
||||
|
||||
@ -230,6 +227,16 @@ f_callclobbered_calleesaved:
|
||||
ret x19
|
||||
.size f_callclobbered_calleesaved, .-f_callclobbered_calleesaved
|
||||
|
||||
.globl f_unreachable_instruction
|
||||
.type f_unreachable_instruction,@function
|
||||
f_unreachable_instruction:
|
||||
// CHECK-LABEL: GS-PAUTH: Warning: unreachable instruction found in function f_unreachable_instruction, basic block {{[0-9a-zA-Z.]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: add x0, x1, x2
|
||||
b 1f
|
||||
add x0, x1, x2
|
||||
1:
|
||||
ret
|
||||
.size f_unreachable_instruction, .-f_unreachable_instruction
|
||||
|
||||
/// Now do a basic sanity check on every different Authentication instruction:
|
||||
|
||||
|
@ -17,9 +17,8 @@ f_crossbb1:
|
||||
.size f_crossbb1, .-f_crossbb1
|
||||
// CHECK-LABEL: GS-PAUTH: non-protected ret found in function f_crossbb1, basic block {{[^,]+}}, at address
|
||||
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: ret
|
||||
// CHECK-NEXT: The 2 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
|
||||
// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
|
||||
// CHECK-NEXT: 2. {{[0-9a-f]+}}: autiasp
|
||||
|
||||
// A test that checks that the dataflow state tracking across when merging BBs
|
||||
// seems to work:
|
||||
|
@ -40,26 +40,26 @@ simple:
|
||||
// CHECK-NEXT: <empty>
|
||||
// CHECK-NEXT: End of Function "simple"
|
||||
// CHECK-EMPTY:
|
||||
// CHECK-NEXT: PacRetAnalysis::ComputeNext( hint #25, pacret-state<NonAutClobRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (pacret-state<NonAutClobRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: PacRetAnalysis::ComputeNext( b [[BB1]], pacret-state<NonAutClobRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: .. result: (pacret-state<NonAutClobRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: PacRetAnalysis::ComputeNext( hint #25, pacret-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: .. result: (pacret-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: PacRetAnalysis::ComputeNext( b [[BB1]], pacret-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (pacret-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: PacRetAnalysis::Confluence(
|
||||
// CHECK-NEXT: State 1: pacret-state<NonAutClobRegs: , Insts: >
|
||||
// CHECK-NEXT: State 2: pacret-state<NonAutClobRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: merged state: pacret-state<NonAutClobRegs: LR W30 W30_HI , Insts: >
|
||||
// CHECK-NEXT: PacRetAnalysis::ComputeNext( hint #29, pacret-state<NonAutClobRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: .. result: (pacret-state<NonAutClobRegs: , Insts: >)
|
||||
// CHECK-NEXT: PacRetAnalysis::ComputeNext( ret x30, pacret-state<NonAutClobRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (pacret-state<NonAutClobRegs: , Insts: >)
|
||||
// CHECK-NEXT: State 1: pacret-state<empty>
|
||||
// CHECK-NEXT: State 2: pacret-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: merged state: pacret-state<SafeToDerefRegs: , Insts: >
|
||||
// CHECK-NEXT: PacRetAnalysis::ComputeNext( hint #29, pacret-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (pacret-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: PacRetAnalysis::ComputeNext( ret x30, pacret-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: .. result: (pacret-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: PacRetAnalysis::Confluence(
|
||||
// CHECK-NEXT: State 1: pacret-state<NonAutClobRegs: LR W30 W30_HI , Insts: >
|
||||
// CHECK-NEXT: State 2: pacret-state<NonAutClobRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: merged state: pacret-state<NonAutClobRegs: LR W30 W30_HI , Insts: >
|
||||
// CHECK-NEXT: PacRetAnalysis::ComputeNext( hint #29, pacret-state<NonAutClobRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: .. result: (pacret-state<NonAutClobRegs: , Insts: >)
|
||||
// CHECK-NEXT: PacRetAnalysis::ComputeNext( ret x30, pacret-state<NonAutClobRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (pacret-state<NonAutClobRegs: , Insts: >)
|
||||
// CHECK-NEXT: State 1: pacret-state<SafeToDerefRegs: , Insts: >
|
||||
// CHECK-NEXT: State 2: pacret-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: merged state: pacret-state<SafeToDerefRegs: , Insts: >
|
||||
// CHECK-NEXT: PacRetAnalysis::ComputeNext( hint #29, pacret-state<SafeToDerefRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (pacret-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: PacRetAnalysis::ComputeNext( ret x30, pacret-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: .. result: (pacret-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: After PacRetAnalysis:
|
||||
// CHECK-NEXT: Binary Function "simple" {
|
||||
// CHECK-NEXT: Number : 1
|
||||
@ -69,24 +69,23 @@ simple:
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: [[BB0]] (2 instructions, align : 1)
|
||||
// CHECK-NEXT: Entry Point
|
||||
// CHECK-NEXT: 00000000: paciasp # PacRetAnalysis: pacret-state<NonAutClobRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: 00000004: b [[BB1]] # PacRetAnalysis: pacret-state<NonAutClobRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: 00000000: paciasp # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: 00000004: b [[BB1]] # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: Successors: [[BB1]]
|
||||
// CHECK-EMPTY:
|
||||
// CHECK-NEXT: [[BB1]] (2 instructions, align : 1)
|
||||
// CHECK-NEXT: Predecessors: [[BB0]]
|
||||
// CHECK-NEXT: 00000008: autiasp # PacRetAnalysis: pacret-state<NonAutClobRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: 0000000c: ret # PacRetAnalysis: pacret-state<NonAutClobRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: 00000008: autiasp # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: 0000000c: ret # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
|
||||
// CHECK-EMPTY:
|
||||
// CHECK-NEXT: DWARF CFI Instructions:
|
||||
// CHECK-NEXT: <empty>
|
||||
// CHECK-NEXT: End of Function "simple"
|
||||
// CHECK-EMPTY:
|
||||
// CHECK-NEXT: Found RET inst: 00000000: ret # PacRetAnalysis: pacret-state<NonAutClobRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: Found RET inst: 00000000: ret # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: RetReg: LR
|
||||
// CHECK-NEXT: Authenticated reg: (none)
|
||||
// CHECK-NEXT: NonAutClobRegs at Ret:{{[ \t]*$}}
|
||||
// CHECK-NEXT: Intersection with RetReg:{{[ \t]*$}}
|
||||
// CHECK-NEXT: SafeToDerefRegs: LR W30 W30_HI{{[ \t]*$}}
|
||||
|
||||
.globl clobber
|
||||
.type clobber,@function
|
||||
@ -97,10 +96,10 @@ clobber:
|
||||
|
||||
// CHECK-LABEL:Analyzing in function clobber, AllocatorId 1
|
||||
// ...
|
||||
// CHECK: PacRetAnalysis::ComputeNext( mov w30, #0x0, pacret-state<NonAutClobRegs: , Insts: >)
|
||||
// CHECK-NEXT: .. result: (pacret-state<NonAutClobRegs: W30 , Insts: >)
|
||||
// CHECK-NEXT: PacRetAnalysis::ComputeNext( ret x30, pacret-state<NonAutClobRegs: W30 , Insts: >)
|
||||
// CHECK-NEXT: .. result: (pacret-state<NonAutClobRegs: W30 , Insts: >)
|
||||
// CHECK: PacRetAnalysis::ComputeNext( mov w30, #0x0, pacret-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
|
||||
// CHECK-NEXT: .. result: (pacret-state<SafeToDerefRegs: W30_HI , Insts: >)
|
||||
// CHECK-NEXT: PacRetAnalysis::ComputeNext( ret x30, pacret-state<SafeToDerefRegs: W30_HI , Insts: >)
|
||||
// CHECK-NEXT: .. result: (pacret-state<SafeToDerefRegs: W30_HI , Insts: >)
|
||||
// CHECK-NEXT: After PacRetAnalysis:
|
||||
// CHECK-NEXT: Binary Function "clobber" {
|
||||
// ...
|
||||
@ -109,15 +108,14 @@ clobber:
|
||||
// The above output was printed after first run of analysis
|
||||
|
||||
// CHECK-EMPTY:
|
||||
// CHECK-NEXT: Found RET inst: 00000000: ret # PacRetAnalysis: pacret-state<NonAutClobRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: Found RET inst: 00000000: ret # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
|
||||
// CHECK-NEXT: RetReg: LR
|
||||
// CHECK-NEXT: Authenticated reg: (none)
|
||||
// CHECK-NEXT: NonAutClobRegs at Ret: W30
|
||||
// CHECK-NEXT: Intersection with RetReg: W30
|
||||
// CHECK-NEXT: PacRetAnalysis::ComputeNext( mov w30, #0x0, pacret-state<NonAutClobRegs: , Insts: [0]()>)
|
||||
// CHECK-NEXT: .. result: (pacret-state<NonAutClobRegs: W30 , Insts: [0](0x{{[0-9a-f]+}} )>)
|
||||
// CHECK-NEXT: PacRetAnalysis::ComputeNext( ret x30, pacret-state<NonAutClobRegs: W30 , Insts: [0](0x{{[0-9a-f]+}} )>)
|
||||
// CHECK-NEXT: .. result: (pacret-state<NonAutClobRegs: W30 , Insts: [0](0x{{[0-9a-f]+}} )>)
|
||||
// CHECK-NEXT: SafeToDerefRegs: W30_HI{{[ \t]*$}}
|
||||
// CHECK-NEXT: PacRetAnalysis::ComputeNext( mov w30, #0x0, pacret-state<SafeToDerefRegs: LR W30 W30_HI , Insts: [0]()>)
|
||||
// CHECK-NEXT: .. result: (pacret-state<SafeToDerefRegs: W30_HI , Insts: [0](0x{{[0-9a-f]+}} )>)
|
||||
// CHECK-NEXT: PacRetAnalysis::ComputeNext( ret x30, pacret-state<SafeToDerefRegs: W30_HI , Insts: [0](0x{{[0-9a-f]+}} )>)
|
||||
// CHECK-NEXT: .. result: (pacret-state<SafeToDerefRegs: W30_HI , Insts: [0](0x{{[0-9a-f]+}} )>)
|
||||
// CHECK-NEXT: After detailed PacRetAnalysis:
|
||||
// CHECK-NEXT: Binary Function "clobber" {
|
||||
// ...
|
||||
@ -127,7 +125,7 @@ clobber:
|
||||
// Iterating over the reports and attaching clobbering info:
|
||||
|
||||
// CHECK-EMPTY:
|
||||
// CHECK-NEXT: Attaching clobbering info to: 00000000: ret # PacRetAnalysis: pacret-state<NonAutClobRegs: BitVector, Insts: [0](0x{{[0-9a-f]+}} )>
|
||||
// CHECK-NEXT: Attaching clobbering info to: 00000000: ret # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: [0](0x{{[0-9a-f]+}} )>
|
||||
|
||||
|
||||
// CHECK-LABEL:Analyzing in function main, AllocatorId 1
|
||||
|
Loading…
x
Reference in New Issue
Block a user