Skip to content

Commit 0b39fd4

Browse files
committed
[BOLT] Gadget scanner: detect non-protected indirect calls
1 parent 479bf68 commit 0b39fd4

File tree

5 files changed

+796
-14
lines changed

5 files changed

+796
-14
lines changed

bolt/include/bolt/Core/MCPlusBuilder.h

+10
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,16 @@ class MCPlusBuilder {
577577
return getNoRegister();
578578
}
579579

580+
/// Returns the register used as call destination, or no-register, if not
581+
/// an indirect call. Sets IsAuthenticatedInternally if the instruction
582+
/// accepts signed pointer as its operand and authenticates it internally.
583+
virtual MCPhysReg
584+
getRegUsedAsCallDest(const MCInst &Inst,
585+
bool &IsAuthenticatedInternally) const {
586+
llvm_unreachable("not implemented");
587+
return getNoRegister();
588+
}
589+
580590
virtual bool isTerminator(const MCInst &Inst) const;
581591

582592
virtual bool isNoop(const MCInst &Inst) const {

bolt/lib/Passes/PAuthGadgetScanner.cpp

+29-4
Original file line numberDiff line numberDiff line change
@@ -382,11 +382,11 @@ class PacRetAnalysis
382382

383383
public:
384384
std::vector<MCInstReference>
385-
getLastClobberingInsts(const MCInst Ret, BinaryFunction &BF,
386-
const ArrayRef<MCPhysReg> UsedDirtyRegs) const {
385+
getLastClobberingInsts(const MCInst &Inst, BinaryFunction &BF,
386+
const ArrayRef<MCPhysReg> UsedDirtyRegs) {
387387
if (RegsToTrackInstsFor.empty())
388388
return {};
389-
auto MaybeState = getStateAt(Ret);
389+
auto MaybeState = getStateBefore(Inst);
390390
if (!MaybeState)
391391
llvm_unreachable("Expected State to be present");
392392
const State &S = *MaybeState;
@@ -434,6 +434,29 @@ shouldReportReturnGadget(const BinaryContext &BC, const MCInstReference &Inst,
434434
return std::make_shared<GadgetReport>(RetKind, Inst, RetReg);
435435
}
436436

437+
static std::shared_ptr<Report>
438+
shouldReportCallGadget(const BinaryContext &BC, const MCInstReference &Inst,
439+
const State &S) {
440+
static const GadgetKind CallKind("non-protected call found");
441+
if (!BC.MIB->isCall(Inst) && !BC.MIB->isBranch(Inst))
442+
return nullptr;
443+
444+
bool IsAuthenticated = false;
445+
MCPhysReg DestReg = BC.MIB->getRegUsedAsCallDest(Inst, IsAuthenticated);
446+
if (IsAuthenticated || DestReg == BC.MIB->getNoRegister())
447+
return nullptr;
448+
449+
LLVM_DEBUG({
450+
traceInst(BC, "Found call inst", Inst);
451+
traceReg(BC, "Call destination reg", DestReg);
452+
traceRegMask(BC, "SafeToDerefRegs", S.SafeToDerefRegs);
453+
});
454+
if (S.SafeToDerefRegs[DestReg])
455+
return nullptr;
456+
457+
return std::make_shared<GadgetReport>(CallKind, Inst, DestReg);
458+
}
459+
437460
FunctionAnalysisResult
438461
Analysis::findGadgets(BinaryFunction &BF,
439462
MCPlusBuilder::AllocatorIdTy AllocatorId) {
@@ -450,10 +473,12 @@ Analysis::findGadgets(BinaryFunction &BF,
450473
for (BinaryBasicBlock &BB : BF) {
451474
for (int64_t I = 0, E = BB.size(); I < E; ++I) {
452475
MCInstReference Inst(&BB, I);
453-
const State &S = *PRA.getStateAt(Inst);
476+
const State &S = *PRA.getStateBefore(Inst);
454477

455478
if (auto Report = shouldReportReturnGadget(BC, Inst, S))
456479
Result.Diagnostics.push_back(Report);
480+
if (auto Report = shouldReportCallGadget(BC, Inst, S))
481+
Result.Diagnostics.push_back(Report);
457482
}
458483
}
459484
return Result;

bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp

+42
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,48 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
277277
}
278278
}
279279

280+
MCPhysReg
281+
getRegUsedAsCallDest(const MCInst &Inst,
282+
bool &IsAuthenticatedInternally) const override {
283+
assert(isCall(Inst) || isBranch(Inst));
284+
IsAuthenticatedInternally = false;
285+
286+
switch (Inst.getOpcode()) {
287+
case AArch64::B:
288+
case AArch64::BL:
289+
assert(Inst.getOperand(0).isExpr());
290+
return getNoRegister();
291+
case AArch64::Bcc:
292+
case AArch64::CBNZW:
293+
case AArch64::CBNZX:
294+
case AArch64::CBZW:
295+
case AArch64::CBZX:
296+
assert(Inst.getOperand(1).isExpr());
297+
return getNoRegister();
298+
case AArch64::TBNZW:
299+
case AArch64::TBNZX:
300+
case AArch64::TBZW:
301+
case AArch64::TBZX:
302+
assert(Inst.getOperand(2).isExpr());
303+
return getNoRegister();
304+
case AArch64::BR:
305+
case AArch64::BLR:
306+
return Inst.getOperand(0).getReg();
307+
case AArch64::BRAA:
308+
case AArch64::BRAB:
309+
case AArch64::BRAAZ:
310+
case AArch64::BRABZ:
311+
case AArch64::BLRAA:
312+
case AArch64::BLRAB:
313+
case AArch64::BLRAAZ:
314+
case AArch64::BLRABZ:
315+
IsAuthenticatedInternally = true;
316+
return Inst.getOperand(0).getReg();
317+
default:
318+
llvm_unreachable("Unhandled call instruction");
319+
}
320+
}
321+
280322
bool isADRP(const MCInst &Inst) const override {
281323
return Inst.getOpcode() == AArch64::ADRP;
282324
}

0 commit comments

Comments
 (0)