Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit b787d6d

Browse files
committedNov 23, 2024
Enhance KCFI type IDs with a 3-bit arity indicator.
1 parent 53a6a11 commit b787d6d

File tree

6 files changed

+95
-21
lines changed

6 files changed

+95
-21
lines changed
 

‎clang/lib/CodeGen/CodeGenModule.cpp

+28-3
Original file line numberDiff line numberDiff line change
@@ -2183,7 +2183,8 @@ llvm::ConstantInt *CodeGenModule::CreateCrossDsoCfiTypeId(llvm::Metadata *MD) {
21832183
}
21842184

21852185
llvm::ConstantInt *CodeGenModule::CreateKCFITypeId(QualType T) {
2186-
if (auto *FnType = T->getAs<FunctionProtoType>())
2186+
auto *FnType = T->getAs<FunctionProtoType>();
2187+
if (FnType)
21872188
T = getContext().getFunctionType(
21882189
FnType->getReturnType(), FnType->getParamTypes(),
21892190
FnType->getExtProtoInfo().withExceptionSpec(EST_None));
@@ -2196,8 +2197,32 @@ llvm::ConstantInt *CodeGenModule::CreateKCFITypeId(QualType T) {
21962197
if (getCodeGenOpts().SanitizeCfiICallNormalizeIntegers)
21972198
Out << ".normalized";
21982199

2199-
return llvm::ConstantInt::get(Int32Ty,
2200-
static_cast<uint32_t>(llvm::xxHash64(OutName)));
2200+
uint32_t OutHash = static_cast<uint32_t>(llvm::xxHash64(OutName));
2201+
const auto &Triple = getTarget().getTriple();
2202+
if (FnType && Triple.isX86() && Triple.isArch64Bit() && Triple.isOSLinux()) {
2203+
// Estimate the function's arity (i.e., the number of arguments) at the ABI
2204+
// level by counting the number of parameters that are likely to be passed
2205+
// as registers, such as pointers and 64-bit (or smaller) integers. The
2206+
// Linux x86-64 ABI allows up to 6 parameters to be passed in GPRs.
2207+
// Additional parameters or parameters larger than 64 bits may be passed on
2208+
// the stack, in which case the arity is denoted as 7.
2209+
bool MayHaveStackArgs = FnType->getNumParams() > 6;
2210+
2211+
for (unsigned int i = 0; !MayHaveStackArgs && i < FnType->getNumParams();
2212+
++i) {
2213+
const Type *PT = FnType->getParamType(i).getTypePtr();
2214+
if (!(PT->isPointerType() || (PT->isIntegralOrEnumerationType() &&
2215+
getContext().getTypeSize(PT) <= 64)))
2216+
MayHaveStackArgs = true;
2217+
}
2218+
2219+
// The 3-bit arity is concatenated with the lower 29 bits of the KCFI hash
2220+
// to form an enhanced KCFI type ID. This can prevent, for example, a
2221+
// 3-arity function's ID from ever colliding with a 2-arity function's ID.
2222+
OutHash = (OutHash << 3) | (MayHaveStackArgs ? 7 : FnType->getNumParams());
2223+
}
2224+
2225+
return llvm::ConstantInt::get(Int32Ty, OutHash);
22012226
}
22022227

22032228
void CodeGenModule::SetLLVMFunctionAttributes(GlobalDecl GD,

‎clang/test/CodeGen/kcfi-normalize.c

+12-6
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,31 @@
1010
void foo(void (*fn)(int), int arg) {
1111
// CHECK-LABEL: define{{.*}}foo
1212
// CHECK-SAME: {{.*}}!kcfi_type ![[TYPE1:[0-9]+]]
13-
// CHECK: call void %0(i32 noundef %1){{.*}}[ "kcfi"(i32 1162514891) ]
13+
// KCFI ID = 0x2A548E59
14+
// CHECK: call void %0(i32 noundef %1){{.*}}[ "kcfi"(i32 710184537) ]
1415
fn(arg);
1516
}
1617

1718
void bar(void (*fn)(int, int), int arg1, int arg2) {
1819
// CHECK-LABEL: define{{.*}}bar
1920
// CHECK-SAME: {{.*}}!kcfi_type ![[TYPE2:[0-9]+]]
20-
// CHECK: call void %0(i32 noundef %1, i32 noundef %2){{.*}}[ "kcfi"(i32 448046469) ]
21+
// KCFI ID = 0xD5A52C2A
22+
// CHECK: call void %0(i32 noundef %1, i32 noundef %2){{.*}}[ "kcfi"(i32 -710595542) ]
2123
fn(arg1, arg2);
2224
}
2325

2426
void baz(void (*fn)(int, int, int), int arg1, int arg2, int arg3) {
2527
// CHECK-LABEL: define{{.*}}baz
2628
// CHECK-SAME: {{.*}}!kcfi_type ![[TYPE3:[0-9]+]]
27-
// CHECK: call void %0(i32 noundef %1, i32 noundef %2, i32 noundef %3){{.*}}[ "kcfi"(i32 -2049681433) ]
29+
// KCFI ID = 0x2EA2BF3B
30+
// CHECK: call void %0(i32 noundef %1, i32 noundef %2, i32 noundef %3){{.*}}[ "kcfi"(i32 782417723) ]
2831
fn(arg1, arg2, arg3);
2932
}
3033

3134
// CHECK: ![[#]] = !{i32 4, !"cfi-normalize-integers", i32 1}
32-
// CHECK: ![[TYPE1]] = !{i32 -1143117868}
33-
// CHECK: ![[TYPE2]] = !{i32 -460921415}
34-
// CHECK: ![[TYPE3]] = !{i32 -333839615}
35+
// KCFI ID = DEEB3EA2
36+
// CHECK: ![[TYPE1]] = !{i32 -555008350}
37+
// KCFI ID = 24372DCB
38+
// CHECK: ![[TYPE2]] = !{i32 607595979}
39+
// KCFI ID = 0x60D0180C
40+
// CHECK: ![[TYPE3]] = !{i32 1624250380}

‎clang/test/CodeGen/kcfi.c

+19-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
/// Must emit __kcfi_typeid symbols for address-taken function declarations
99
// CHECK: module asm ".weak __kcfi_typeid_[[F4:[a-zA-Z0-9_]+]]"
10-
// CHECK: module asm ".set __kcfi_typeid_[[F4]], [[#%d,HASH:]]"
1110
/// Must not __kcfi_typeid symbols for non-address-taken declarations
1211
// CHECK-NOT: module asm ".weak __kcfi_typeid_{{f6|_Z2f6v}}"
1312

@@ -29,7 +28,7 @@ int __call(fn_t f) __attribute__((__no_sanitize__("kcfi"))) {
2928

3029
// CHECK: define dso_local{{.*}} i32 @{{call|_Z4callPFivE}}(ptr{{.*}} %f){{.*}}
3130
int call(fn_t f) {
32-
// CHECK: call{{.*}} i32 %{{.}}(){{.*}} [ "kcfi"(i32 [[#HASH]]) ]
31+
// CHECK: call{{.*}} i32 %{{.}}(){{.*}} [ "kcfi"(i32 [[#%d,HASH:]]) ]
3332
return f();
3433
}
3534

@@ -48,6 +47,20 @@ static int f5(void) { return 2; }
4847
// CHECK-DAG: declare !kcfi_type ![[#TYPE]]{{.*}} i32 @{{f6|_Z2f6v}}()
4948
extern int f6(void);
5049

50+
typedef struct {
51+
int *p1;
52+
int *p2[16];
53+
} s_t;
54+
55+
// CHECK: define internal{{.*}} i32 @{{f7|_ZL2f7PFi3s_tEPS_}}(ptr{{.*}} %f, ptr{{.*}} %s){{.*}}
56+
static int f7(int (*f)(s_t), s_t *s) {
57+
// CHECK: call{{.*}} i32 %{{.*}} [ "kcfi"(i32 [[#%d,HASH4:]]) ]
58+
return f(*s) + 1;
59+
}
60+
61+
// CHECK: define internal{{.*}} i32 @{{f8|_ZL2f83s_t}}(ptr{{.*}} %s){{.*}} !kcfi_type ![[#%d,TYPE4:]]
62+
static int f8(s_t s) { return 0; }
63+
5164
#ifndef __cplusplus
5265
// C: define internal ptr @resolver1() #[[#]] !kcfi_type ![[#]] {
5366
int ifunc1(int) __attribute__((ifunc("resolver1")));
@@ -59,12 +72,14 @@ long ifunc2(long) __attribute__((ifunc("resolver2")));
5972
#endif
6073

6174
int test(void) {
75+
s_t s;
6276
return call(f1) +
6377
__call((fn_t)f2) +
6478
call(f3) +
6579
call(f4) +
6680
f5() +
67-
f6();
81+
f6() +
82+
f7(f8, &s);
6883
}
6984

7085
#ifdef __cplusplus
@@ -85,3 +100,4 @@ void test_member_call(void) {
85100
// CHECK-DAG: ![[#TYPE]] = !{i32 [[#HASH]]}
86101
// CHECK-DAG: ![[#TYPE2]] = !{i32 [[#%d,HASH2:]]}
87102
// MEMBER-DAG: ![[#TYPE3]] = !{i32 [[#HASH3]]}
103+
// CHECK-DAG: ![[#TYPE4]] = !{i32 [[#HASH4]]}

‎llvm/lib/Transforms/Utils/ModuleUtils.cpp

+28-3
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "llvm/Transforms/Utils/ModuleUtils.h"
14-
#include "llvm/Analysis/VectorUtils.h"
1514
#include "llvm/ADT/SmallString.h"
15+
#include "llvm/Analysis/VectorUtils.h"
1616
#include "llvm/IR/DerivedTypes.h"
1717
#include "llvm/IR/Function.h"
1818
#include "llvm/IR/IRBuilder.h"
@@ -21,6 +21,7 @@
2121
#include "llvm/Support/MD5.h"
2222
#include "llvm/Support/raw_ostream.h"
2323
#include "llvm/Support/xxhash.h"
24+
#include "llvm/TargetParser/Triple.h"
2425

2526
using namespace llvm;
2627

@@ -208,10 +209,34 @@ void llvm::setKCFIType(Module &M, Function &F, StringRef MangledType) {
208209
std::string Type = MangledType.str();
209210
if (M.getModuleFlag("cfi-normalize-integers"))
210211
Type += ".normalized";
212+
213+
uint32_t OutHash = static_cast<uint32_t>(llvm::xxHash64(Type));
214+
Triple T(M.getTargetTriple());
215+
if (T.isX86() && T.isArch64Bit() && T.isOSLinux()) {
216+
// Estimate the function's arity (i.e., the number of arguments) at the ABI
217+
// level by counting the number of parameters that are likely to be passed
218+
// as registers, such as pointers and 64-bit (or smaller) integers. The
219+
// Linux x86-64 ABI allows up to 6 parameters to be passed in GPRs.
220+
// Additional parameters or parameters larger than 64 bits may be passed on
221+
// the stack, in which case the arity is denoted as 7.
222+
size_t NumParams = F.arg_size();
223+
bool MayHaveStackArgs = NumParams > 6;
224+
225+
for (unsigned int i = 0; !MayHaveStackArgs && i < NumParams; ++i) {
226+
const llvm::Type *PT = F.getArg(i)->getType();
227+
if (!(PT->isPointerTy() || PT->getIntegerBitWidth() <= 64))
228+
MayHaveStackArgs = true;
229+
}
230+
231+
// The 3-bit arity is concatenated with the lower 29 bits of the KCFI hash
232+
// to form an enhanced KCFI type ID. This can prevent, for example, a
233+
// 3-arity function's ID from ever colliding with a 2-arity function's ID.
234+
OutHash = (OutHash << 3) | (MayHaveStackArgs ? 7 : NumParams);
235+
}
236+
211237
F.setMetadata(LLVMContext::MD_kcfi_type,
212238
MDNode::get(Ctx, MDB.createConstant(ConstantInt::get(
213-
Type::getInt32Ty(Ctx),
214-
static_cast<uint32_t>(xxHash64(Type))))));
239+
Type::getInt32Ty(Ctx), OutHash))));
215240
// If the module was compiled with -fpatchable-function-entry, ensure
216241
// we use the same patchable-function-prefix.
217242
if (auto *MD = mdconst::extract_or_null<ConstantInt>(

‎llvm/test/Transforms/GCOVProfiling/kcfi-normalize.ll

+4-3
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
; RUN: mkdir -p %t && cd %t
44
; RUN: opt < %s -S -passes=insert-gcov-profiling \
55
; RUN: -mtriple=x86_64-unknown-linux-gnu | FileCheck \
6-
; RUN: --check-prefixes=CHECK,CHECK-CTOR-INIT %s
6+
; RUN: --check-prefixes=CHECK,CHECK-CTOR-INIT,CHECK-X86 %s
77
; RUN: opt < %s -S -passes=insert-gcov-profiling \
88
; RUN: -mtriple=powerpc64-ibm-aix | FileCheck \
9-
; RUN: --check-prefixes=CHECK,CHECK-RT-INIT %s
9+
; RUN: --check-prefixes=CHECK,CHECK-RT-INIT,CHECK-PPC %s
1010

1111
; Check for gcov initialization function pointers when we initialize
1212
; the writeout and reset functions in the runtime.
@@ -39,4 +39,5 @@ entry:
3939
; CHECK-CTOR-INIT: define internal void @__llvm_gcov_init()
4040
; CHECK-CTOR-INIT-SAME: !kcfi_type ![[#TYPE]]
4141

42-
; CHECK: ![[#TYPE]] = !{i32 -440107680}
42+
; CHECK-PPC: ![[#TYPE]] = !{i32 -440107680}
43+
; CHECK-X86: ![[#TYPE]] = !{i32 774105856}

‎llvm/test/Transforms/GCOVProfiling/kcfi.ll

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
; RUN: mkdir -p %t && cd %t
33
; RUN: opt < %s -S -passes=insert-gcov-profiling \
44
; RUN: -mtriple=x86_64-unknown-linux-gnu | FileCheck \
5-
; RUN: --check-prefixes=CHECK,CHECK-CTOR-INIT %s
5+
; RUN: --check-prefixes=CHECK,CHECK-CTOR-INIT,CHECK-X86 %s
66
; RUN: opt < %s -S -passes=insert-gcov-profiling \
77
; RUN: -mtriple=powerpc64-ibm-aix | FileCheck \
8-
; RUN: --check-prefixes=CHECK,CHECK-RT-INIT %s
8+
; RUN: --check-prefixes=CHECK,CHECK-RT-INIT,CHECK-PPC %s
99

1010
; Check for gcov initialization function pointers when we initialize
1111
; the writeout and reset functions in the runtime.
@@ -37,4 +37,5 @@ entry:
3737
; CHECK-CTOR-INIT: define internal void @__llvm_gcov_init()
3838
; CHECK-CTOR-INIT-SAME: !kcfi_type ![[#TYPE]]
3939

40-
; CHECK: ![[#TYPE]] = !{i32 -1522505972}
40+
; CHECK-PPC: ![[#TYPE]] = !{i32 -1522505972}
41+
; CHECK-X86: ![[#TYPE]] = !{i32 704854112}

0 commit comments

Comments
 (0)
Failed to load comments.