Skip to content

Commit d5fae5e

Browse files
kbeylsbgergely0
authored andcommitted
[GadgetScanner/pacret] Handle noreturn functions.
1 parent 6217b66 commit d5fae5e

File tree

7 files changed

+111
-30
lines changed

7 files changed

+111
-30
lines changed

bolt/include/bolt/Core/MCPlusBuilder.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "bolt/Core/MCPlus.h"
1818
#include "bolt/Core/Relocation.h"
19+
#include "bolt/Utils/CommandLineOpts.h"
1920
#include "llvm/ADT/ArrayRef.h"
2021
#include "llvm/ADT/BitVector.h"
2122
#include "llvm/ADT/StringMap.h"
@@ -27,6 +28,7 @@
2728
#include "llvm/MC/MCInstrAnalysis.h"
2829
#include "llvm/MC/MCInstrDesc.h"
2930
#include "llvm/MC/MCInstrInfo.h"
31+
#include "llvm/MC/MCSymbol.h"
3032
#include "llvm/Support/Allocator.h"
3133
#include "llvm/Support/Casting.h"
3234
#include "llvm/Support/ErrorHandling.h"
@@ -546,6 +548,27 @@ class MCPlusBuilder {
546548
return Analysis->isCall(Inst) || isTailCall(Inst);
547549
}
548550

551+
virtual std::optional<StringRef> getCalleeName(const MCInst &Inst) const {
552+
assert(isCall(Inst));
553+
if (MCPlus::getNumPrimeOperands(Inst) != 1 || !Inst.getOperand(0).isExpr())
554+
return {};
555+
556+
const MCSymbol *CalleeSymbol = getTargetSymbol(Inst);
557+
assert(CalleeSymbol != nullptr);
558+
return CalleeSymbol->getName();
559+
}
560+
561+
virtual bool isNoReturnCall(const MCInst &Inst) const {
562+
if (!isCall(Inst))
563+
return false;
564+
auto calleeName = getCalleeName(Inst);
565+
if (calleeName)
566+
for (std::string &Name : opts::AssumeNoReturnFunctions)
567+
if (calleeName->equals(Name))
568+
return true;
569+
return false;
570+
}
571+
549572
virtual bool isReturn(const MCInst &Inst) const {
550573
return Analysis->isReturn(Inst);
551574
}

bolt/include/bolt/Utils/CommandLineOpts.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ extern llvm::cl::opt<bool> TimeOpts;
6262
extern llvm::cl::opt<bool> UseOldText;
6363
extern llvm::cl::opt<bool> UpdateDebugSections;
6464

65+
extern llvm::cl::list<std::string> AssumeNoReturnFunctions;
66+
extern llvm::cl::opt<std::string> AssumeNoReturnFunctionsFile;
67+
68+
/// Reads names from FunctionNamesFile and adds them to FunctionNames.
69+
void populateFunctionNames(const llvm::cl::opt<std::string> &FunctionNamesFile,
70+
llvm::cl::list<std::string> &FunctionNames);
71+
6572
// The default verbosity level (0) is pretty terse, level 1 is fairly
6673
// verbose and usually prints some informational message for every
6774
// function processed. Level 2 is for the noisiest of messages and

bolt/lib/Core/BinaryFunction.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2155,7 +2155,8 @@ Error BinaryFunction::buildCFG(MCPlusBuilder::AllocatorIdTy AllocatorId) {
21552155
addCFIPlaceholders(Offset, InsertBB);
21562156
}
21572157

2158-
const bool IsBlockEnd = MIB->isTerminator(Instr);
2158+
const bool IsBlockEnd =
2159+
MIB->isTerminator(Instr) || MIB->isNoReturnCall(Instr);
21592160
IsLastInstrNop = MIB->isNoop(Instr);
21602161
if (!IsLastInstrNop)
21612162
LastInstrOffset = Offset;
@@ -2242,8 +2243,11 @@ Error BinaryFunction::buildCFG(MCPlusBuilder::AllocatorIdTy AllocatorId) {
22422243
//
22432244
// Conditional tail call is a special case since we don't add a taken
22442245
// branch successor for it.
2245-
IsPrevFT = !MIB->isTerminator(*LastInstr) ||
2246-
MIB->getConditionalTailCall(*LastInstr);
2246+
if (MIB->isNoReturnCall(*LastInstr))
2247+
IsPrevFT = false;
2248+
else
2249+
IsPrevFT = !MIB->isTerminator(*LastInstr) ||
2250+
MIB->getConditionalTailCall(*LastInstr);
22472251
} else if (BB->succ_size() == 1) {
22482252
IsPrevFT = MIB->isConditionalBranch(*LastInstr);
22492253
} else {

bolt/lib/Passes/BinaryPasses.cpp

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1852,17 +1852,16 @@ Error InlineMemcpy::runOnFunctions(BinaryContext &BC) {
18521852
for (auto II = BB.begin(); II != BB.end(); ++II) {
18531853
MCInst &Inst = *II;
18541854

1855-
if (!BC.MIB->isCall(Inst) || MCPlus::getNumPrimeOperands(Inst) != 1 ||
1856-
!Inst.getOperand(0).isExpr())
1855+
if (!BC.MIB->isCall(Inst))
18571856
continue;
1858-
1859-
const MCSymbol *CalleeSymbol = BC.MIB->getTargetSymbol(Inst);
1860-
if (CalleeSymbol->getName() != "memcpy" &&
1861-
CalleeSymbol->getName() != "memcpy@PLT" &&
1862-
CalleeSymbol->getName() != "_memcpy8")
1857+
std::optional<StringRef> CalleeName = BC.MIB->getCalleeName(Inst);
1858+
if (!CalleeName)
1859+
continue;
1860+
if (*CalleeName != "memcpy" && *CalleeName != "memcpy@PLT" &&
1861+
*CalleeName != "_memcpy8")
18631862
continue;
18641863

1865-
const bool IsMemcpy8 = (CalleeSymbol->getName() == "_memcpy8");
1864+
const bool IsMemcpy8 = (*CalleeName == "_memcpy8");
18661865
const bool IsTailCall = BC.MIB->isTailCall(Inst);
18671866

18681867
const InstructionListType NewCode =
@@ -1951,13 +1950,12 @@ Error SpecializeMemcpy1::runOnFunctions(BinaryContext &BC) {
19511950
for (auto II = CurBB->begin(); II != CurBB->end(); ++II) {
19521951
MCInst &Inst = *II;
19531952

1954-
if (!BC.MIB->isCall(Inst) || MCPlus::getNumPrimeOperands(Inst) != 1 ||
1955-
!Inst.getOperand(0).isExpr())
1953+
if (!BC.MIB->isCall(Inst))
19561954
continue;
1957-
1958-
const MCSymbol *CalleeSymbol = BC.MIB->getTargetSymbol(Inst);
1959-
if (CalleeSymbol->getName() != "memcpy" &&
1960-
CalleeSymbol->getName() != "memcpy@PLT")
1955+
std::optional<StringRef> CalleeName = BC.MIB->getCalleeName(Inst);
1956+
if (!CalleeName)
1957+
continue;
1958+
if (*CalleeName != "memcpy" && *CalleeName != "memcpy@PLT")
19611959
continue;
19621960

19631961
if (BC.MIB->isTailCall(Inst))

bolt/lib/Rewrite/RewriteInstance.cpp

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,9 @@ MCPlusBuilder *createMCPlusBuilder(const Triple::ArchType Arch,
269269
const MCInstrInfo *Info,
270270
const MCRegisterInfo *RegInfo,
271271
const MCSubtargetInfo *STI) {
272+
opts::populateFunctionNames(opts::AssumeNoReturnFunctionsFile,
273+
opts::AssumeNoReturnFunctions);
274+
272275
#ifdef X86_AVAILABLE
273276
if (Arch == Triple::x86_64)
274277
return createX86MCPlusBuilder(Analysis, Info, RegInfo, STI);
@@ -2929,18 +2932,12 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
29292932

29302933
void RewriteInstance::selectFunctionsToProcess() {
29312934
// Extend the list of functions to process or skip from a file.
2932-
auto populateFunctionNames = [](cl::opt<std::string> &FunctionNamesFile,
2933-
cl::list<std::string> &FunctionNames) {
2934-
if (FunctionNamesFile.empty())
2935-
return;
2936-
std::ifstream FuncsFile(FunctionNamesFile, std::ios::in);
2937-
std::string FuncName;
2938-
while (std::getline(FuncsFile, FuncName))
2939-
FunctionNames.push_back(FuncName);
2940-
};
2941-
populateFunctionNames(opts::FunctionNamesFile, opts::ForceFunctionNames);
2942-
populateFunctionNames(opts::SkipFunctionNamesFile, opts::SkipFunctionNames);
2943-
populateFunctionNames(opts::FunctionNamesFileNR, opts::ForceFunctionNamesNR);
2935+
opts::populateFunctionNames(opts::FunctionNamesFile,
2936+
opts::ForceFunctionNames);
2937+
opts::populateFunctionNames(opts::SkipFunctionNamesFile,
2938+
opts::SkipFunctionNames);
2939+
opts::populateFunctionNames(opts::FunctionNamesFileNR,
2940+
opts::ForceFunctionNamesNR);
29442941

29452942
// Make a set of functions to process to speed up lookups.
29462943
std::unordered_set<std::string> ForceFunctionsNR(

bolt/lib/Utils/CommandLineOpts.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "bolt/Utils/CommandLineOpts.h"
14-
#include "VCSVersion.inc"
14+
#include "llvm/Support/VCSRevision.h"
15+
#include <fstream>
1516

1617
using namespace llvm;
1718

@@ -206,11 +207,31 @@ cl::opt<bool> UpdateDebugSections(
206207
cl::desc("update DWARF debug sections of the executable"),
207208
cl::cat(BoltCategory));
208209

210+
cl::list<std::string> AssumeNoReturnFunctions(
211+
"noreturnfuncs", cl::CommaSeparated,
212+
cl::desc("List which function names to assume are no-return"),
213+
cl::value_desc("func1,func2,func3,..."), cl::Hidden, cl::cat(BoltCategory));
214+
215+
cl::opt<std::string> AssumeNoReturnFunctionsFile(
216+
"noreturnfuncs-file",
217+
cl::desc("file with list of functions to assume are no-return"), cl::Hidden,
218+
cl::cat(BoltCategory));
219+
209220
cl::opt<unsigned>
210221
Verbosity("v", cl::desc("set verbosity level for diagnostic output"),
211222
cl::init(0), cl::ZeroOrMore, cl::cat(BoltCategory),
212223
cl::sub(cl::SubCommand::getAll()));
213224

225+
void populateFunctionNames(const cl::opt<std::string> &FunctionNamesFile,
226+
cl::list<std::string> &FunctionNames) {
227+
if (FunctionNamesFile.empty())
228+
return;
229+
std::ifstream FuncsFile(FunctionNamesFile, std::ios::in);
230+
std::string FuncName;
231+
while (std::getline(FuncsFile, FuncName))
232+
FunctionNames.push_back(FuncName);
233+
}
234+
214235
bool processAllFunctions() {
215236
if (opts::AggregateOnly)
216237
return false;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Check that there are no false positives related to no-return functions.
2+
3+
// RUN: %clang %cflags -march=armv8.3-a -mbranch-protection=pac-ret %s %p/../Inputs/asm_main.c -o %t.exe
4+
// RUN: llvm-bolt-gadget-scanner %t.exe --noreturnfuncs="doesnotreturn/1" 2>&1 | FileCheck -check-prefix=CHECK --allow-empty %s
5+
6+
7+
// Verify that we can also detect gadgets across basic blocks
8+
9+
.globl f_call_returning
10+
.type f_call_returning,@function
11+
f_call_returning:
12+
bl call_returning
13+
ret
14+
.size f_call_returning, .-f_call_returning
15+
// CHECK-LABEL: GS-PACRET: non-protected ret found in function f_call_returning, basic block .L{{[^,]+}}, at address
16+
// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: ret
17+
// CHECK-NEXT: The 1 instructions that write to the return register after any authentication are:
18+
// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl call_returning
19+
20+
.type doesnotreturn,@function
21+
doesnotreturn:
22+
brk 1
23+
.size doesnotreturn, .-doesnotreturn
24+
25+
.globl f_call_noreturn
26+
.type f_call_noreturn,@function
27+
f_call_noreturn:
28+
bl doesnotreturn
29+
ret
30+
.size f_call_noreturn, .-f_call_noreturn
31+
// CHECK-NOT: function f_call_noreturn

0 commit comments

Comments
 (0)