Skip to content

Commit 4fb9709

Browse files
Implement a new kcfi_arity feature that encodes an indirect call target's arity (i.e., the number of live-in registers) in the function's __cfi header.
1 parent 5d6d982 commit 4fb9709

File tree

11 files changed

+268
-1
lines changed

11 files changed

+268
-1
lines changed

clang/docs/ControlFlowIntegrity.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,15 @@ cross-DSO function address equality. These properties make KCFI easier to
336336
adopt in low-level software. KCFI is limited to checking only function
337337
pointers, and isn't compatible with executable-only memory.
338338

339+
``-fsanitize-kcfi-arity``
340+
-----------------------------
341+
342+
For supported targets, this feature extends kCFI by telling the compiler to
343+
record information about each indirect-callable function's arity (i.e., the
344+
number of arguments passed in registers) into the binary. Some kernel CFI
345+
techniques, such as FineIBT, may be able to use this information to provide
346+
enhanced security.
347+
339348
Member Function Pointer Call Checking
340349
=====================================
341350

clang/docs/UsersManual.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2220,6 +2220,12 @@ are listed below.
22202220

22212221
This option is currently experimental.
22222222

2223+
.. option:: -fsanitize-kcfi-arity
2224+
2225+
Extends kernel indirect call forward-edge control flow integrity with
2226+
additional function arity information (for supported targets). See
2227+
:doc:`ControlFlowIntegrity` for more details.
2228+
22232229
.. option:: -fstrict-vtable-pointers
22242230

22252231
Enable optimizations based on the strict rules for overwriting polymorphic

clang/include/clang/Basic/CodeGenOptions.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ CODEGENOPT(SanitizeCfiICallNormalizeIntegers, 1, 0) ///< Normalize integer types
277277
///< CFI icall function signatures
278278
CODEGENOPT(SanitizeCfiCanonicalJumpTables, 1, 0) ///< Make jump table symbols canonical
279279
///< instead of creating a local jump table.
280+
CODEGENOPT(SanitizeKcfiArity, 1, 0) ///< Embed arity in KCFI patchable function prefix
280281
CODEGENOPT(SanitizeCoverageType, 2, 0) ///< Type of sanitizer coverage
281282
///< instrumentation.
282283
CODEGENOPT(SanitizeCoverageIndirectCalls, 1, 0) ///< Enable sanitizer coverage

clang/include/clang/Basic/Features.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ FEATURE(is_trivially_constructible, LangOpts.CPlusPlus)
254254
FEATURE(is_trivially_copyable, LangOpts.CPlusPlus)
255255
FEATURE(is_union, LangOpts.CPlusPlus)
256256
FEATURE(kcfi, LangOpts.Sanitize.has(SanitizerKind::KCFI))
257+
FEATURE(kcfi_arity, LangOpts.Sanitize.has(SanitizerKind::KCFI))
257258
FEATURE(modules, LangOpts.Modules)
258259
FEATURE(safe_stack, LangOpts.Sanitize.has(SanitizerKind::SafeStack))
259260
FEATURE(shadow_call_stack,

clang/include/clang/Driver/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2619,6 +2619,10 @@ defm sanitize_cfi_canonical_jump_tables : BoolOption<"f", "sanitize-cfi-canonica
26192619
"Do not make">,
26202620
BothFlags<[], [ClangOption], " the jump table addresses canonical in the symbol table">>,
26212621
Group<f_clang_Group>;
2622+
def fsanitize_kcfi_arity : Flag<["-"], "fsanitize-kcfi-arity">,
2623+
Group<f_clang_Group>,
2624+
HelpText<"Embed function arity information into the KCFI patchable function prefix">,
2625+
MarshallingInfoFlag<CodeGenOpts<"SanitizeKcfiArity">>;
26222626
defm sanitize_stats : BoolOption<"f", "sanitize-stats",
26232627
CodeGenOpts<"SanitizeStats">, DefaultFalse,
26242628
PosFlag<SetTrue, [], [ClangOption], "Enable">,

clang/include/clang/Driver/SanitizerArgs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class SanitizerArgs {
4343
bool CfiICallGeneralizePointers = false;
4444
bool CfiICallNormalizeIntegers = false;
4545
bool CfiCanonicalJumpTables = false;
46+
bool KcfiArity = false;
4647
int AsanFieldPadding = 0;
4748
bool SharedRuntime = false;
4849
bool StableABI = false;

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1149,6 +1149,8 @@ void CodeGenModule::Release() {
11491149
if (CodeGenOpts.PatchableFunctionEntryOffset)
11501150
getModule().addModuleFlag(llvm::Module::Override, "kcfi-offset",
11511151
CodeGenOpts.PatchableFunctionEntryOffset);
1152+
if (CodeGenOpts.SanitizeKcfiArity)
1153+
getModule().addModuleFlag(llvm::Module::Override, "kcfi-arity", 1);
11521154
}
11531155

11541156
if (CodeGenOpts.CFProtectionReturn &&

clang/lib/Driver/SanitizerArgs.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,8 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
829829
CfiICallNormalizeIntegers =
830830
Args.hasArg(options::OPT_fsanitize_cfi_icall_normalize_integers);
831831

832+
KcfiArity = Args.hasArg(options::OPT_fsanitize_kcfi_arity);
833+
832834
if (AllAddedKinds & SanitizerKind::CFI && DiagnoseErrors)
833835
D.Diag(diag::err_drv_argument_not_allowed_with)
834836
<< "-fsanitize=kcfi"
@@ -1383,6 +1385,14 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
13831385
if (CfiICallNormalizeIntegers)
13841386
CmdArgs.push_back("-fsanitize-cfi-icall-experimental-normalize-integers");
13851387

1388+
if (KcfiArity) {
1389+
if (!TC.getTriple().isOSLinux() || !TC.getTriple().isArch64Bit()) {
1390+
TC.getDriver().Diag(clang::diag::err_drv_kcfi_arity_unsupported_target)
1391+
<< TC.getTriple().str();
1392+
}
1393+
CmdArgs.push_back("-fsanitize-kcfi-arity");
1394+
}
1395+
13861396
if (CfiCanonicalJumpTables)
13871397
CmdArgs.push_back("-fsanitize-cfi-canonical-jump-tables");
13881398

clang/test/CodeGen/kcfi-arity.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fsanitize=kcfi -fsanitize-kcfi-arity -o - %s | FileCheck %s
2+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fsanitize=kcfi -fsanitize-kcfi-arity -x c++ -o - %s | FileCheck %s
3+
#if !__has_feature(kcfi_arity)
4+
#error Missing kcfi_arity?
5+
#endif
6+
7+
// CHECK: ![[#]] = !{i32 4, !"kcfi-arity", i32 1}

llvm/lib/Target/X86/X86AsmPrinter.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,29 @@ void X86AsmPrinter::emitKCFITypeId(const MachineFunction &MF) {
181181
// Embed the type hash in the X86::MOV32ri instruction to avoid special
182182
// casing object file parsers.
183183
EmitKCFITypePadding(MF);
184+
unsigned DestReg = X86::EAX;
185+
186+
if (F.getParent()->getModuleFlag("kcfi-arity")) {
187+
// The ArityToRegMap assumes the 64-bit Linux kernel ABI
188+
const auto &Triple = MF.getTarget().getTargetTriple();
189+
assert(Triple.isArch64Bit() && Triple.isOSLinux());
190+
191+
// Determine the function's arity (i.e., the number of arguments) at the ABI
192+
// level by counting the number of parameters that are passed
193+
// as registers, such as pointers and 64-bit (or smaller) integers. The
194+
// Linux x86-64 ABI allows up to 6 parameters to be passed in GPRs.
195+
// Additional parameters or parameters larger than 64 bits may be passed on
196+
// the stack, in which case the arity is denoted as 7.
197+
const unsigned ArityToRegMap[8] = {X86::EAX, X86::ECX, X86::EDX, X86::EBX,
198+
X86::ESP, X86::EBP, X86::ESI, X86::EDI};
199+
int Arity = MF.getInfo<X86MachineFunctionInfo>()->getArgumentStackSize() > 0
200+
? 7
201+
: MF.getRegInfo().liveins().size();
202+
DestReg = ArityToRegMap[Arity];
203+
}
204+
184205
EmitAndCountInstruction(MCInstBuilder(X86::MOV32ri)
185-
.addReg(X86::EAX)
206+
.addReg(DestReg)
186207
.addImm(MaskKCFIType(Type->getZExtValue())));
187208

188209
if (MAI->hasDotTypeDotSizeDirective()) {

0 commit comments

Comments
 (0)