diff --git a/clang/docs/ControlFlowIntegrity.rst b/clang/docs/ControlFlowIntegrity.rst --- a/clang/docs/ControlFlowIntegrity.rst +++ b/clang/docs/ControlFlowIntegrity.rst @@ -236,6 +236,25 @@ ``-fsanitize-cfi-icall-generalize-pointers`` is not compatible with ``-fsanitize-cfi-cross-dso``. +.. _cfi-icall-experimental-normalize-integers: + +``-fsanitize-cfi-icall-experimental-normalize-integers`` +-------------------------------------------------------- + +This option enables normalizing integer types as vendor extended types for +cross-language LLVM CFI/KCFI support with other languages that can't represent +and encode C/C++ integer types. + +Specifically, integer types are encoded as their defined representations (e.g., +8-bit signed integer, 16-bit signed integer, 32-bit signed integer, ...) for +compatibility with languages that define explicitly-sized integer types (e.g., +i8, i16, i32, ..., in Rust). + +``-fsanitize-cfi-icall-experimental-normalize-integers`` is compatible with +``-fsanitize-cfi-icall-generalize-pointers``. + +This option is currently experimental. + .. _cfi-canonical-jump-tables: ``-fsanitize-cfi-canonical-jump-tables`` diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -1992,6 +1992,14 @@ checked by Control Flow Integrity indirect call checking. See :doc:`ControlFlowIntegrity` for more details. +.. option:: -fsanitize-cfi-icall-experimental-normalize-integers + + Normalize integers in return and argument types in function type signatures + checked by Control Flow Integrity indirect call checking. See + :doc:`ControlFlowIntegrity` for more details. + + This option is currently experimental. + .. option:: -fstrict-vtable-pointers Enable optimizations based on the strict rules for overwriting polymorphic diff --git a/clang/include/clang/AST/Mangle.h b/clang/include/clang/AST/Mangle.h --- a/clang/include/clang/AST/Mangle.h +++ b/clang/include/clang/AST/Mangle.h @@ -140,7 +140,8 @@ unsigned ManglingNumber, raw_ostream &) = 0; virtual void mangleCXXRTTI(QualType T, raw_ostream &) = 0; - virtual void mangleCXXRTTIName(QualType T, raw_ostream &) = 0; + virtual void mangleCXXRTTIName(QualType T, raw_ostream &, + bool NormalizeIntegers = false) = 0; virtual void mangleStringLiteral(const StringLiteral *SL, raw_ostream &) = 0; virtual void mangleMSGuidDecl(const MSGuidDecl *GD, raw_ostream&); @@ -177,7 +178,8 @@ /// or type uniquing. /// TODO: Extend this to internal types by generating names that are unique /// across translation units so it can be used with LTO. - virtual void mangleTypeName(QualType T, raw_ostream &) = 0; + virtual void mangleTypeName(QualType T, raw_ostream &, + bool NormalizeIntegers = false) = 0; /// @} }; diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -257,6 +257,8 @@ ///< diagnostics. CODEGENOPT(SanitizeCfiICallGeneralizePointers, 1, 0) ///< Generalize pointer types in ///< CFI icall function signatures +CODEGENOPT(SanitizeCfiICallNormalizeIntegers, 1, 0) ///< Normalize integer types in + ///< CFI icall function signatures CODEGENOPT(SanitizeCfiCanonicalJumpTables, 1, 0) ///< Make jump table symbols canonical ///< instead of creating a local jump table. CODEGENOPT(SanitizeCoverageType, 2, 0) ///< Type of sanitizer coverage diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1869,6 +1869,10 @@ Group, HelpText<"Generalize pointers in CFI indirect call type signature checks">, MarshallingInfoFlag>; +def fsanitize_cfi_icall_normalize_integers : Flag<["-"], "fsanitize-cfi-icall-experimental-normalize-integers">, + Group, + HelpText<"Normalize integers in CFI indirect call type signature checks">, + MarshallingInfoFlag>; defm sanitize_cfi_canonical_jump_tables : BoolOption<"f", "sanitize-cfi-canonical-jump-tables", CodeGenOpts<"SanitizeCfiCanonicalJumpTables">, DefaultFalse, PosFlag, NegFlag, diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h --- a/clang/include/clang/Driver/SanitizerArgs.h +++ b/clang/include/clang/Driver/SanitizerArgs.h @@ -37,6 +37,7 @@ bool MsanParamRetval = true; bool CfiCrossDso = false; bool CfiICallGeneralizePointers = false; + bool CfiICallNormalizeIntegers = false; bool CfiCanonicalJumpTables = false; int AsanFieldPadding = 0; bool SharedRuntime = false; diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -109,8 +109,10 @@ void mangleCXXCtorVTable(const CXXRecordDecl *RD, int64_t Offset, const CXXRecordDecl *Type, raw_ostream &) override; void mangleCXXRTTI(QualType T, raw_ostream &) override; - void mangleCXXRTTIName(QualType T, raw_ostream &) override; - void mangleTypeName(QualType T, raw_ostream &) override; + void mangleCXXRTTIName(QualType T, raw_ostream &, + bool NormalizeIntegers) override; + void mangleTypeName(QualType T, raw_ostream &, + bool NormalizeIntegers) override; void mangleCXXCtorComdat(const CXXConstructorDecl *D, raw_ostream &) override; void mangleCXXDtorComdat(const CXXDestructorDecl *D, raw_ostream &) override; @@ -215,6 +217,10 @@ class CXXNameMangler { ItaniumMangleContextImpl &Context; raw_ostream &Out; + /// Normalize integer types for cross-language CFI support with other + /// languages that can't represent and encode C/C++ integer types. + bool NormalizeIntegers = false; + bool NullOut = false; /// In the "DisableDerivedAbiTags" mode derived ABI tags are not calculated. /// This mode is used when mangler creates another mangler recursively to @@ -413,6 +419,10 @@ : Context(C), Out(Out_), Structor(getStructor(D)), StructorType(Type), AbiTagsRoot(AbiTags) {} + CXXNameMangler(ItaniumMangleContextImpl &C, raw_ostream &Out_, + bool NormalizeIntegers_) + : Context(C), Out(Out_), NormalizeIntegers(NormalizeIntegers_), + NullOut(false), AbiTagsRoot(AbiTags) {} CXXNameMangler(CXXNameMangler &Outer, raw_ostream &Out_) : Context(Outer.Context), Out(Out_), Structor(Outer.Structor), StructorType(Outer.StructorType), SeqID(Outer.SeqID), @@ -2937,6 +2947,85 @@ // ::= Dn # std::nullptr_t (i.e., decltype(nullptr)) // ::= u # vendor extended type std::string type_name; + // Normalize integer types as vendor extended types: + // ui + // uu + if (NormalizeIntegers && T->isInteger()) { + if (T->isSignedInteger()) { + switch (getASTContext().getTypeSize(T)) { + case 8: + // Pick a representative for each integer size in the substitution + // dictionary. (Its actual defined size is not relevant.) + if (mangleSubstitution(BuiltinType::SChar)) + break; + Out << "u2i8"; + addSubstitution(BuiltinType::SChar); + break; + case 16: + if (mangleSubstitution(BuiltinType::Short)) + break; + Out << "u3i16"; + addSubstitution(BuiltinType::Short); + break; + case 32: + if (mangleSubstitution(BuiltinType::Int)) + break; + Out << "u3i32"; + addSubstitution(BuiltinType::Int); + break; + case 64: + if (mangleSubstitution(BuiltinType::Long)) + break; + Out << "u3i64"; + addSubstitution(BuiltinType::Long); + break; + case 128: + if (mangleSubstitution(BuiltinType::Int128)) + break; + Out << "u4i128"; + addSubstitution(BuiltinType::Int128); + break; + default: + llvm_unreachable("Unknown integer size for normalization"); + } + } else { + switch (getASTContext().getTypeSize(T)) { + case 8: + if (mangleSubstitution(BuiltinType::UChar)) + break; + Out << "u2u8"; + addSubstitution(BuiltinType::UChar); + break; + case 16: + if (mangleSubstitution(BuiltinType::UShort)) + break; + Out << "u3u16"; + addSubstitution(BuiltinType::UShort); + break; + case 32: + if (mangleSubstitution(BuiltinType::UInt)) + break; + Out << "u3u32"; + addSubstitution(BuiltinType::UInt); + break; + case 64: + if (mangleSubstitution(BuiltinType::ULong)) + break; + Out << "u3u64"; + addSubstitution(BuiltinType::ULong); + break; + case 128: + if (mangleSubstitution(BuiltinType::UInt128)) + break; + Out << "u4u128"; + addSubstitution(BuiltinType::UInt128); + break; + default: + llvm_unreachable("Unknown integer size for normalization"); + } + } + return; + } switch (T->getKind()) { case BuiltinType::Void: Out << 'v'; @@ -6529,16 +6618,17 @@ Mangler.mangleType(Ty); } -void ItaniumMangleContextImpl::mangleCXXRTTIName(QualType Ty, - raw_ostream &Out) { +void ItaniumMangleContextImpl::mangleCXXRTTIName( + QualType Ty, raw_ostream &Out, bool NormalizeIntegers = false) { // ::= TS # typeinfo name (null terminated byte string) - CXXNameMangler Mangler(*this, Out); + CXXNameMangler Mangler(*this, Out, NormalizeIntegers); Mangler.getStream() << "_ZTS"; Mangler.mangleType(Ty); } -void ItaniumMangleContextImpl::mangleTypeName(QualType Ty, raw_ostream &Out) { - mangleCXXRTTIName(Ty, Out); +void ItaniumMangleContextImpl::mangleTypeName(QualType Ty, raw_ostream &Out, + bool NormalizeIntegers = false) { + mangleCXXRTTIName(Ty, Out, NormalizeIntegers); } void ItaniumMangleContextImpl::mangleStringLiteral(const StringLiteral *, raw_ostream &) { diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -180,7 +180,8 @@ int32_t VBPtrOffset, uint32_t VBIndex, raw_ostream &Out) override; void mangleCXXRTTI(QualType T, raw_ostream &Out) override; - void mangleCXXRTTIName(QualType T, raw_ostream &Out) override; + void mangleCXXRTTIName(QualType T, raw_ostream &Out, + bool NormalizeIntegers) override; void mangleCXXRTTIBaseClassDescriptor(const CXXRecordDecl *Derived, uint32_t NVOffset, int32_t VBPtrOffset, uint32_t VBTableOffset, uint32_t Flags, @@ -193,7 +194,8 @@ mangleCXXRTTICompleteObjectLocator(const CXXRecordDecl *Derived, ArrayRef BasePath, raw_ostream &Out) override; - void mangleTypeName(QualType T, raw_ostream &) override; + void mangleTypeName(QualType T, raw_ostream &, + bool NormalizeIntegers) override; void mangleReferenceTemporary(const VarDecl *, unsigned ManglingNumber, raw_ostream &) override; void mangleStaticGuardVariable(const VarDecl *D, raw_ostream &Out) override; @@ -3593,8 +3595,8 @@ Mangler.getStream() << "@8"; } -void MicrosoftMangleContextImpl::mangleCXXRTTIName(QualType T, - raw_ostream &Out) { +void MicrosoftMangleContextImpl::mangleCXXRTTIName( + QualType T, raw_ostream &Out, bool NormalizeIntegers = false) { MicrosoftCXXNameMangler Mangler(*this, Out); Mangler.getStream() << '.'; Mangler.mangleType(T, SourceRange(), MicrosoftCXXNameMangler::QMM_Result); @@ -3761,7 +3763,8 @@ Mangler.mangleName(EnclosingDecl); } -void MicrosoftMangleContextImpl::mangleTypeName(QualType T, raw_ostream &Out) { +void MicrosoftMangleContextImpl::mangleTypeName( + QualType T, raw_ostream &Out, bool NormalizeIntegers = false) { // This is just a made up unique string for the purposes of tbaa. undname // does *not* know how to demangle it. MicrosoftCXXNameMangler Mangler(*this, Out); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1727,7 +1727,11 @@ std::string OutName; llvm::raw_string_ostream Out(OutName); - getCXXABI().getMangleContext().mangleTypeName(T, Out); + getCXXABI().getMangleContext().mangleTypeName( + T, Out, getCodeGenOpts().SanitizeCfiICallNormalizeIntegers); + + if (getCodeGenOpts().SanitizeCfiICallNormalizeIntegers) + Out << ".normalized"; return llvm::ConstantInt::get(Int32Ty, static_cast(llvm::xxHash64(OutName))); @@ -6945,7 +6949,12 @@ if (isExternallyVisible(T->getLinkage())) { std::string OutName; llvm::raw_string_ostream Out(OutName); - getCXXABI().getMangleContext().mangleTypeName(T, Out); + getCXXABI().getMangleContext().mangleTypeName( + T, Out, getCodeGenOpts().SanitizeCfiICallNormalizeIntegers); + + if (getCodeGenOpts().SanitizeCfiICallNormalizeIntegers) + Out << ".normalized"; + Out << Suffix; InternalId = llvm::MDString::get(getLLVMContext(), Out.str()); diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -711,6 +711,9 @@ CfiICallGeneralizePointers = Args.hasArg(options::OPT_fsanitize_cfi_icall_generalize_pointers); + CfiICallNormalizeIntegers = + Args.hasArg(options::OPT_fsanitize_cfi_icall_normalize_integers); + if (CfiCrossDso && CfiICallGeneralizePointers && DiagnoseErrors) D.Diag(diag::err_drv_argument_not_allowed_with) << "-fsanitize-cfi-cross-dso" @@ -1215,6 +1218,9 @@ if (CfiICallGeneralizePointers) CmdArgs.push_back("-fsanitize-cfi-icall-generalize-pointers"); + if (CfiICallNormalizeIntegers) + CmdArgs.push_back("-fsanitize-cfi-icall-experimental-normalize-integers"); + if (CfiCanonicalJumpTables) CmdArgs.push_back("-fsanitize-cfi-canonical-jump-tables"); diff --git a/clang/test/CodeGen/cfi-icall-normalize.c b/clang/test/CodeGen/cfi-icall-normalize.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/cfi-icall-normalize.c @@ -0,0 +1,78 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-icall -fsanitize-trap=cfi-icall -fsanitize-cfi-icall-experimental-normalize-integers -emit-llvm -o - %s | FileCheck %s + +// Test that integer types are normalized for cross-language CFI support with +// other languages that can't represent and encode C/C++ integer types. + +void foo0(char arg) { } +// CHECK: define{{.*}}foo0{{.*}}!type ![[TYPE0:[0-9]+]] !type !{{[0-9]+}} +void foo1(char arg1, signed char arg2) { } +// CHECK: define{{.*}}foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} +void foo2(char arg1, signed char arg2, signed char arg3) { } +// CHECK: define{{.*}}foo2{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} +void foo3(int arg) { } +// CHECK: define{{.*}}foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} +void foo4(int arg1, int arg2) { } +// CHECK: define{{.*}}foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} +void foo5(int arg1, int arg2, int arg3) { } +// CHECK: define{{.*}}foo5{{.*}}!type ![[TYPE5:[0-9]+]] !type !{{[0-9]+}} +void foo6(long arg) { } +// CHECK: define{{.*}}foo6{{.*}}!type ![[TYPE6:[0-9]+]] !type !{{[0-9]+}} +void foo7(long arg1, long long arg2) { } +// CHECK: define{{.*}}foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}} +void foo8(long arg1, long long arg2, long long arg3) { } +// CHECK: define{{.*}}foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} +void foo9(short arg) { } +// CHECK: define{{.*}}foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} +void foo10(short arg1, short arg2) { } +// CHECK: define{{.*}}foo10{{.*}}!type ![[TYPE10:[0-9]+]] !type !{{[0-9]+}} +void foo11(short arg1, short arg2, short arg3) { } +// CHECK: define{{.*}}foo11{{.*}}!type ![[TYPE11:[0-9]+]] !type !{{[0-9]+}} +void foo12(unsigned char arg) { } +// CHECK: define{{.*}}foo12{{.*}}!type ![[TYPE12:[0-9]+]] !type !{{[0-9]+}} +void foo13(unsigned char arg1, unsigned char arg2) { } +// CHECK: define{{.*}}foo13{{.*}}!type ![[TYPE13:[0-9]+]] !type !{{[0-9]+}} +void foo14(unsigned char arg1, unsigned char arg2, unsigned char arg3) { } +// CHECK: define{{.*}}foo14{{.*}}!type ![[TYPE14:[0-9]+]] !type !{{[0-9]+}} +void foo15(unsigned int arg) { } +// CHECK: define{{.*}}foo15{{.*}}!type ![[TYPE15:[0-9]+]] !type !{{[0-9]+}} +void foo16(unsigned int arg1, unsigned int arg2) { } +// CHECK: define{{.*}}foo16{{.*}}!type ![[TYPE16:[0-9]+]] !type !{{[0-9]+}} +void foo17(unsigned int arg1, unsigned int arg2, unsigned int arg3) { } +// CHECK: define{{.*}}foo17{{.*}}!type ![[TYPE17:[0-9]+]] !type !{{[0-9]+}} +void foo18(unsigned long arg) { } +// CHECK: define{{.*}}foo18{{.*}}!type ![[TYPE18:[0-9]+]] !type !{{[0-9]+}} +void foo19(unsigned long arg1, unsigned long long arg2) { } +// CHECK: define{{.*}}foo19{{.*}}!type ![[TYPE19:[0-9]+]] !type !{{[0-9]+}} +void foo20(unsigned long arg1, unsigned long long arg2, unsigned long long arg3) { } +// CHECK: define{{.*}}foo20{{.*}}!type ![[TYPE20:[0-9]+]] !type !{{[0-9]+}} +void foo21(unsigned short arg) { } +// CHECK: define{{.*}}foo21{{.*}}!type ![[TYPE21:[0-9]+]] !type !{{[0-9]+}} +void foo22(unsigned short arg1, unsigned short arg2) { } +// CHECK: define{{.*}}foo22{{.*}}!type ![[TYPE22:[0-9]+]] !type !{{[0-9]+}} +void foo23(unsigned short arg1, unsigned short arg2, unsigned short arg3) { } +// CHECK: define{{.*}}foo23{{.*}}!type ![[TYPE23:[0-9]+]] !type !{{[0-9]+}} + +// CHECK: ![[TYPE0]] = !{i64 0, !"_ZTSFvu2i8E.normalized"} +// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvu2i8S_E.normalized"} +// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvu2i8S_S_E.normalized"} +// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvu3i32E.normalized"} +// CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvu3i32S_E.normalized"} +// CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvu3i32S_S_E.normalized"} +// CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvu3i64E.normalized"} +// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvu3i64S_E.normalized"} +// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvu3i64S_S_E.normalized"} +// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu3i16E.normalized"} +// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvu3i16S_E.normalized"} +// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvu3i16S_S_E.normalized"} +// CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFvu2u8E.normalized"} +// CHECK: ![[TYPE13]] = !{i64 0, !"_ZTSFvu2u8S_E.normalized"} +// CHECK: ![[TYPE14]] = !{i64 0, !"_ZTSFvu2u8S_S_E.normalized"} +// CHECK: ![[TYPE15]] = !{i64 0, !"_ZTSFvu3u32E.normalized"} +// CHECK: ![[TYPE16]] = !{i64 0, !"_ZTSFvu3u32S_E.normalized"} +// CHECK: ![[TYPE17]] = !{i64 0, !"_ZTSFvu3u32S_S_E.normalized"} +// CHECK: ![[TYPE18]] = !{i64 0, !"_ZTSFvu3u64E.normalized"} +// CHECK: ![[TYPE19]] = !{i64 0, !"_ZTSFvu3u64S_E.normalized"} +// CHECK: ![[TYPE20]] = !{i64 0, !"_ZTSFvu3u64S_S_E.normalized"} +// CHECK: ![[TYPE21]] = !{i64 0, !"_ZTSFvu3u16E.normalized"} +// CHECK: ![[TYPE22]] = !{i64 0, !"_ZTSFvu3u16S_E.normalized"} +// CHECK: ![[TYPE23]] = !{i64 0, !"_ZTSFvu3u16S_S_E.normalized"} diff --git a/clang/test/CodeGen/cfi-icall-normalize2.c b/clang/test/CodeGen/cfi-icall-normalize2.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/cfi-icall-normalize2.c @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-icall -fsanitize-trap=cfi-icall -fsanitize-cfi-icall-experimental-normalize-integers -emit-llvm -o - %s | FileCheck %s + +// Test that normalized type metadata for functions are emitted for cross-language CFI support with +// other languages that can't represent and encode C/C++ integer types. + +void foo(void (*fn)(int), int arg) { + // CHECK-LABEL: define{{.*}}foo + // CHECK-SAME: {{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} + // CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%0}}, metadata !"_ZTSFvu3i32E.normalized") + fn(arg); +} + +void bar(void (*fn)(int, int), int arg1, int arg2) { + // CHECK-LABEL: define{{.*}}bar + // CHECK-SAME: {{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} + // CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%0}}, metadata !"_ZTSFvu3i32S_E.normalized") + fn(arg1, arg2); +} + +void baz(void (*fn)(int, int, int), int arg1, int arg2, int arg3) { + // CHECK-LABEL: define{{.*}}baz + // CHECK-SAME: {{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} + // CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%0}}, metadata !"_ZTSFvu3i32S_S_E.normalized") + fn(arg1, arg2, arg3); +} + +// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvPFvu3i32ES_E.normalized"} +// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvPFvu3i32S_ES_S_E.normalized"} +// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvPFvu3i32S_S_ES_S_S_E.normalized"} diff --git a/clang/test/CodeGen/kcfi-normalize.c b/clang/test/CodeGen/kcfi-normalize.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/kcfi-normalize.c @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fsanitize=kcfi -fsanitize-cfi-icall-experimental-normalize-integers -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fsanitize=kcfi -fsanitize-cfi-icall-experimental-normalize-integers -x c++ -o - %s | FileCheck %s +#if !__has_feature(kcfi) +#error Missing kcfi? +#endif + +// Test that normalized type metadata for functions are emitted for cross-language KCFI support with +// other languages that can't represent and encode C/C++ integer types. + +void foo(void (*fn)(int), int arg) { + // CHECK-LABEL: define{{.*}}foo + // CHECK-SAME: {{.*}}!kcfi_type ![[TYPE1:[0-9]+]] + // CHECK: call void %0(i32 noundef %1){{.*}}[ "kcfi"(i32 1162514891) ] + fn(arg); +} + +void bar(void (*fn)(int, int), int arg1, int arg2) { + // CHECK-LABEL: define{{.*}}bar + // CHECK-SAME: {{.*}}!kcfi_type ![[TYPE2:[0-9]+]] + // CHECK: call void %0(i32 noundef %1, i32 noundef %2){{.*}}[ "kcfi"(i32 448046469) ] + fn(arg1, arg2); +} + +void baz(void (*fn)(int, int, int), int arg1, int arg2, int arg3) { + // CHECK-LABEL: define{{.*}}baz + // CHECK-SAME: {{.*}}!kcfi_type ![[TYPE3:[0-9]+]] + // CHECK: call void %0(i32 noundef %1, i32 noundef %2, i32 noundef %3){{.*}}[ "kcfi"(i32 -2049681433) ] + fn(arg1, arg2, arg3); +} + +// CHECK: ![[TYPE1]] = !{i32 -1143117868} +// CHECK: ![[TYPE2]] = !{i32 -460921415} +// CHECK: ![[TYPE3]] = !{i32 -333839615}