diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 834b1c067d84c..6734f9b20dcab 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -2335,7 +2335,40 @@ llvm::ConstantInt *CodeGenModule::CreateCrossDsoCfiTypeId(llvm::Metadata *MD) { return llvm::ConstantInt::get(Int64Ty, llvm::MD5Hash(MDS->getString())); } +// Generalize pointer types to a void pointer with the qualifiers of the +// originally pointed-to type, e.g. 'const char *' and 'char * const *' +// generalize to 'const void *' while 'char *' and 'const char **' generalize to +// 'void *'. +static QualType GeneralizeType(ASTContext &Ctx, QualType Ty) { + if (!Ty->isPointerType()) + return Ty; + + return Ctx.getPointerType( + QualType(Ctx.VoidTy) + .withCVRQualifiers(Ty->getPointeeType().getCVRQualifiers())); +} + +// Apply type generalization to a FunctionType's return and argument types +static QualType GeneralizeFunctionType(ASTContext &Ctx, QualType Ty) { + if (auto *FnType = Ty->getAs()) { + SmallVector GeneralizedParams; + for (auto &Param : FnType->param_types()) + GeneralizedParams.push_back(GeneralizeType(Ctx, Param)); + + return Ctx.getFunctionType(GeneralizeType(Ctx, FnType->getReturnType()), + GeneralizedParams, FnType->getExtProtoInfo()); + } + + if (auto *FnType = Ty->getAs()) + return Ctx.getFunctionNoProtoType( + GeneralizeType(Ctx, FnType->getReturnType())); + + llvm_unreachable("Encountered unknown FunctionType"); +} + llvm::ConstantInt *CodeGenModule::CreateKCFITypeId(QualType T) { + if (getCodeGenOpts().SanitizeCfiICallGeneralizePointers) + T = GeneralizeFunctionType(getContext(), T); if (auto *FnType = T->getAs()) T = getContext().getFunctionType( FnType->getReturnType(), FnType->getParamTypes(), @@ -2348,6 +2381,8 @@ llvm::ConstantInt *CodeGenModule::CreateKCFITypeId(QualType T) { if (getCodeGenOpts().SanitizeCfiICallNormalizeIntegers) Out << ".normalized"; + if (getCodeGenOpts().SanitizeCfiICallGeneralizePointers) + Out << ".generalized"; return llvm::ConstantInt::get(Int32Ty, static_cast(llvm::xxHash64(OutName))); @@ -7880,38 +7915,6 @@ CodeGenModule::CreateMetadataIdentifierForVirtualMemPtrType(QualType T) { return CreateMetadataIdentifierImpl(T, VirtualMetadataIdMap, ".virtual"); } -// Generalize pointer types to a void pointer with the qualifiers of the -// originally pointed-to type, e.g. 'const char *' and 'char * const *' -// generalize to 'const void *' while 'char *' and 'const char **' generalize to -// 'void *'. -static QualType GeneralizeType(ASTContext &Ctx, QualType Ty) { - if (!Ty->isPointerType()) - return Ty; - - return Ctx.getPointerType( - QualType(Ctx.VoidTy).withCVRQualifiers( - Ty->getPointeeType().getCVRQualifiers())); -} - -// Apply type generalization to a FunctionType's return and argument types -static QualType GeneralizeFunctionType(ASTContext &Ctx, QualType Ty) { - if (auto *FnType = Ty->getAs()) { - SmallVector GeneralizedParams; - for (auto &Param : FnType->param_types()) - GeneralizedParams.push_back(GeneralizeType(Ctx, Param)); - - return Ctx.getFunctionType( - GeneralizeType(Ctx, FnType->getReturnType()), - GeneralizedParams, FnType->getExtProtoInfo()); - } - - if (auto *FnType = Ty->getAs()) - return Ctx.getFunctionNoProtoType( - GeneralizeType(Ctx, FnType->getReturnType())); - - llvm_unreachable("Encountered unknown FunctionType"); -} - llvm::Metadata *CodeGenModule::CreateMetadataIdentifierGeneralized(QualType T) { return CreateMetadataIdentifierImpl(GeneralizeFunctionType(getContext(), T), GeneralizedMetadataIdMap, ".generalized"); diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp index 98793a5bb9979..54f0e63b98070 100644 --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -851,6 +851,8 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, } if (AllAddedKinds & SanitizerKind::KCFI) { + CfiICallGeneralizePointers = + Args.hasArg(options::OPT_fsanitize_cfi_icall_generalize_pointers); CfiICallNormalizeIntegers = Args.hasArg(options::OPT_fsanitize_cfi_icall_normalize_integers); diff --git a/clang/test/CodeGen/kcfi-generalize.c b/clang/test/CodeGen/kcfi-generalize.c new file mode 100644 index 0000000000000..4e32f4f35057c --- /dev/null +++ b/clang/test/CodeGen/kcfi-generalize.c @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=kcfi -fsanitize-trap=kcfi -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=UNGENERALIZED %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=kcfi -fsanitize-trap=kcfi -fsanitize-cfi-icall-generalize-pointers -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=GENERALIZED %s + +// Test that const char* is generalized to const ptr and that char** is +// generalized to ptr + +// CHECK: define{{.*}} ptr @f({{.*}} !kcfi_type [[TYPE:![0-9]+]] +int** f(const char *a, const char **b) { + return (int**)0; +} + +// GENERALIZED: define{{.*}} ptr @f2({{.*}} !kcfi_type [[TYPE]] +// UNGENERALIZED: define{{.*}} ptr @f2({{.*}} !kcfi_type [[TYPE2:![0-9]+]] +int** f2(const int *a, const int **b) { + return (int**)0; +} + +// CHECK: define{{.*}} ptr @f3({{.*}} !kcfi_type [[TYPE3:![0-9]+]] +int** f3(char *a, char **b) { + return (int**)0; +} + +void g(int** (*fp)(const char *, const char **)) { + // UNGENERALIZED: call {{.*}} [ "kcfi"(i32 1296635908) ] + // GENERALIZED: call {{.*}} [ "kcfi"(i32 -49168686) ] + fp(0, 0); +} + +// UNGENERALIZED: [[TYPE]] = !{i32 1296635908} +// GENERALIZED: [[TYPE]] = !{i32 -49168686} + +// UNGENERALIZED: [[TYPE3]] = !{i32 874141567} +// GENERALIZED: [[TYPE3]] = !{i32 954385378} diff --git a/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c index fbe1fd72c84c6..263301ad4466a 100644 --- a/clang/test/Driver/fsanitize.c +++ b/clang/test/Driver/fsanitize.c @@ -794,6 +794,11 @@ // RUN: not %clang --target=x86_64-linux-gnu -fsanitize=cfi-icall -fsanitize-cfi-icall-generalize-pointers -fsanitize-cfi-cross-dso -fvisibility=hidden -flto -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-GENERALIZE-AND-CROSS-DSO // CHECK-CFI-GENERALIZE-AND-CROSS-DSO: error: invalid argument '-fsanitize-cfi-cross-dso' not allowed with '-fsanitize-cfi-icall-generalize-pointers' +// RUN: %clang --target=x86_64-linux-gnu -fsanitize=kcfi -fsanitize-cfi-icall-generalize-pointers -fvisibility=hidden -flto -c -resource-dir=%S/Inputs/resource_dir %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-KCFI-GENERALIZE-POINTERS +// RUN: %clang --target=x86_64-linux-gnu -fsanitize=kcfi -fvisibility=hidden -flto -c -resource-dir=%S/Inputs/resource_dir %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-KCFI-GENERALIZE-POINTERS +// CHECK-KCFI-GENERALIZE-POINTERS: -fsanitize-cfi-icall-generalize-pointers +// CHECK-NO-KCFI-GENERALIZE-POINTERS-NOT: -fsanitize-cfi-icall-generalize-pointers + // RUN: %clang --target=x86_64-linux-gnu -fsanitize=cfi-icall -fsanitize-cfi-canonical-jump-tables -fvisibility=hidden -flto -c -resource-dir=%S/Inputs/resource_dir %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-CANONICAL-JUMP-TABLES // RUN: %clang --target=x86_64-linux-gnu -fsanitize=cfi-icall -fno-sanitize-cfi-canonical-jump-tables -fvisibility=hidden -flto -c %s -resource-dir=%S/Inputs/resource_dir -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-CFI-CANONICAL-JUMP-TABLES // RUN: %clang --target=x86_64-linux-gnu -fsanitize=cfi-icall -fvisibility=hidden -flto -c -resource-dir=%S/Inputs/resource_dir %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-CANONICAL-JUMP-TABLES