Skip to content

Commit 2d82b35

Browse files
committed
[BOLT] Gadget scanner: detect non-protected indirect calls
1 parent b6b40e9 commit 2d82b35

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 a 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
@@ -401,11 +401,11 @@ class PacRetAnalysis
401401

402402
public:
403403
std::vector<MCInstReference>
404-
getLastClobberingInsts(const MCInst Ret, BinaryFunction &BF,
405-
const ArrayRef<MCPhysReg> UsedDirtyRegs) const {
404+
getLastClobberingInsts(const MCInst &Inst, BinaryFunction &BF,
405+
const ArrayRef<MCPhysReg> UsedDirtyRegs) {
406406
if (RegsToTrackInstsFor.empty())
407407
return {};
408-
auto MaybeState = getStateAt(Ret);
408+
auto MaybeState = getStateBefore(Inst);
409409
if (!MaybeState)
410410
llvm_unreachable("Expected State to be present");
411411
const State &S = *MaybeState;
@@ -453,6 +453,29 @@ shouldReportReturnGadget(const BinaryContext &BC, const MCInstReference &Inst,
453453
return std::make_shared<GadgetReport>(RetKind, Inst, RetReg);
454454
}
455455

456+
static std::shared_ptr<Report>
457+
shouldReportCallGadget(const BinaryContext &BC, const MCInstReference &Inst,
458+
const State &S) {
459+
static const GadgetKind CallKind("non-protected call found");
460+
if (!BC.MIB->isCall(Inst) && !BC.MIB->isBranch(Inst))
461+
return nullptr;
462+
463+
bool IsAuthenticated = false;
464+
MCPhysReg DestReg = BC.MIB->getRegUsedAsCallDest(Inst, IsAuthenticated);
465+
if (IsAuthenticated || DestReg == BC.MIB->getNoRegister())
466+
return nullptr;
467+
468+
LLVM_DEBUG({
469+
traceInst(BC, "Found call inst", Inst);
470+
traceReg(BC, "Call destination reg", DestReg);
471+
traceRegMask(BC, "SafeToDerefRegs", S.SafeToDerefRegs);
472+
});
473+
if (S.SafeToDerefRegs[DestReg])
474+
return nullptr;
475+
476+
return std::make_shared<GadgetReport>(CallKind, Inst, DestReg);
477+
}
478+
456479
FunctionAnalysisResult
457480
Analysis::findGadgets(BinaryFunction &BF,
458481
MCPlusBuilder::AllocatorIdTy AllocatorId) {
@@ -469,7 +492,7 @@ Analysis::findGadgets(BinaryFunction &BF,
469492
for (BinaryBasicBlock &BB : BF) {
470493
for (int64_t I = 0, E = BB.size(); I < E; ++I) {
471494
MCInstReference Inst(&BB, I);
472-
const State &S = *PRA.getStateAt(Inst);
495+
const State &S = *PRA.getStateBefore(Inst);
473496

474497
// If non-empty state was never propagated from the entry basic block
475498
// to Inst, assume it to be unreachable and report a warning.
@@ -481,6 +504,8 @@ Analysis::findGadgets(BinaryFunction &BF,
481504

482505
if (auto Report = shouldReportReturnGadget(BC, Inst, S))
483506
Result.Diagnostics.push_back(Report);
507+
if (auto Report = shouldReportCallGadget(BC, Inst, S))
508+
Result.Diagnostics.push_back(Report);
484509
}
485510
}
486511
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)