Index: clang/lib/CodeGen/CGExpr.cpp =================================================================== --- clang/lib/CodeGen/CGExpr.cpp +++ clang/lib/CodeGen/CGExpr.cpp @@ -4473,8 +4473,12 @@ if (llvm::Constant *PrefixSig = CGM.getTargetCodeGenInfo().getUBSanFunctionSignature(CGM)) { SanitizerScope SanScope(this); + // Remove any (C++17) exception specifications, to allow calling e.g. a + // noexcept function through a non-noexcept pointer. + auto ProtoTy = + getTypes().removeNothrowQualification(cast(FnType)); llvm::Constant *FTRTTIConst = - CGM.GetAddrOfRTTIDescriptor(QualType(FnType, 0), /*ForEH=*/true); + CGM.GetAddrOfRTTIDescriptor(ProtoTy, /*ForEH=*/true); llvm::Type *PrefixStructTyElems[] = {PrefixSig->getType(), Int32Ty}; llvm::StructType *PrefixStructTy = llvm::StructType::get( CGM.getLLVMContext(), PrefixStructTyElems, /*isPacked=*/true); Index: clang/lib/CodeGen/CodeGenFunction.cpp =================================================================== --- clang/lib/CodeGen/CodeGenFunction.cpp +++ clang/lib/CodeGen/CodeGenFunction.cpp @@ -921,8 +921,12 @@ if (getLangOpts().CPlusPlus && SanOpts.has(SanitizerKind::Function)) { if (const FunctionDecl *FD = dyn_cast_or_null(D)) { if (llvm::Constant *PrologueSig = getPrologueSignature(CGM, FD)) { + // Remove any (C++17) exception specifications, to allow calling e.g. a + // noexcept function through a non-noexcept pointer. + auto ProtoTy = + getTypes().removeNothrowQualification(cast(FD->getType())); llvm::Constant *FTRTTIConst = - CGM.GetAddrOfRTTIDescriptor(FD->getType(), /*ForEH=*/true); + CGM.GetAddrOfRTTIDescriptor(ProtoTy, /*ForEH=*/true); llvm::Constant *FTRTTIConstEncoded = EncodeAddrForUseInPrologue(Fn, FTRTTIConst); llvm::Constant *PrologueStructElems[] = {PrologueSig, Index: clang/lib/CodeGen/CodeGenTypes.h =================================================================== --- clang/lib/CodeGen/CodeGenTypes.h +++ clang/lib/CodeGen/CodeGenTypes.h @@ -372,7 +372,10 @@ bool isRecordBeingLaidOut(const Type *Ty) const { return RecordsBeingLaidOut.count(Ty); } - + + /// Return the \arg Proto function type with any (C++17) exception + /// specification removed. + QualType removeNothrowQualification(const FunctionProtoType *Proto) const; }; } // end namespace CodeGen Index: clang/lib/CodeGen/CodeGenTypes.cpp =================================================================== --- clang/lib/CodeGen/CodeGenTypes.cpp +++ clang/lib/CodeGen/CodeGenTypes.cpp @@ -782,3 +782,9 @@ bool CodeGenTypes::isZeroInitializable(const RecordDecl *RD) { return getCGRecordLayout(RD).isZeroInitializable(); } + +QualType CodeGenTypes::removeNothrowQualification(const FunctionProtoType *Proto) const { + return Context.getFunctionType( + Proto->getReturnType(), Proto->getParamTypes(), + Proto->getExtProtoInfo().withExceptionSpec(EST_None)); +} Index: clang/lib/CodeGen/ItaniumCXXABI.cpp =================================================================== --- clang/lib/CodeGen/ItaniumCXXABI.cpp +++ clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -3415,7 +3415,7 @@ /// Compute the flags for a __pbase_type_info, and remove the corresponding /// pieces from \p Type. -static unsigned extractPBaseFlags(ASTContext &Ctx, QualType &Type) { +static unsigned extractPBaseFlags(CodeGenTypes &CGT, QualType &Type) { unsigned Flags = 0; if (Type.isConstQualified()) @@ -3433,11 +3433,9 @@ Flags |= ItaniumRTTIBuilder::PTI_Incomplete; if (auto *Proto = Type->getAs()) { - if (Proto->isNothrow(Ctx)) { + if (Proto->isNothrow(CGT.getContext())) { Flags |= ItaniumRTTIBuilder::PTI_Noexcept; - Type = Ctx.getFunctionType( - Proto->getReturnType(), Proto->getParamTypes(), - Proto->getExtProtoInfo().withExceptionSpec(EST_None)); + Type = CGT.removeNothrowQualification(Proto); } } @@ -3450,7 +3448,7 @@ // Itanium C++ ABI 2.9.5p7: // __flags is a flag word describing the cv-qualification and other // attributes of the type pointed to - unsigned Flags = extractPBaseFlags(CGM.getContext(), PointeeTy); + unsigned Flags = extractPBaseFlags(CGM.getTypes(), PointeeTy); llvm::Type *UnsignedIntLTy = CGM.getTypes().ConvertType(CGM.getContext().UnsignedIntTy); @@ -3473,7 +3471,7 @@ // Itanium C++ ABI 2.9.5p7: // __flags is a flag word describing the cv-qualification and other // attributes of the type pointed to. - unsigned Flags = extractPBaseFlags(CGM.getContext(), PointeeTy); + unsigned Flags = extractPBaseFlags(CGM.getTypes(), PointeeTy); const RecordType *ClassType = cast(Ty->getClass()); if (IsIncompleteClassType(ClassType)) Index: clang/test/CodeGenCXX/ubsan-function-noexcept.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/ubsan-function-noexcept.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -std=c++17 -fsanitize=function -emit-llvm -triple x86_64-linux-gnu %s -o - | FileCheck %s + +// Check that typeinfo recorded in function prolog doesn't have "Do" noexcept +// qualifier in its mangled name. +// CHECK: @[[LABEL:[0-9]+]] = private constant i8* bitcast ({ i8*, i8* }* @_ZTIFvvE to i8*) +// CHECK: define void @_Z1fv() #{{.*}} prologue <{ i32, i32 }> <{ i32 {{.*}}, i32 trunc (i64 sub (i64 ptrtoint (i8** @[[LABEL]] to i64), i64 ptrtoint (void ()* @_Z1fv to i64)) to i32) }> +void f() noexcept {} + +// CHECK: define void @_Z1gPDoFvvE +void g(void (*p)() noexcept) { + // Check that reference typeinfo at call site doesn't have "Do" noexcept + // qualifier in its mangled name, either. + // CHECK: icmp eq i8* %{{.*}}, bitcast ({ i8*, i8* }* @_ZTIFvvE to i8*), !nosanitize + p(); +} Index: compiler-rt/test/ubsan/TestCases/TypeCheck/Function/function.cpp =================================================================== --- compiler-rt/test/ubsan/TestCases/TypeCheck/Function/function.cpp +++ compiler-rt/test/ubsan/TestCases/TypeCheck/Function/function.cpp @@ -1,4 +1,4 @@ -// RUN: %clangxx -fsanitize=function %s -O3 -g -o %t +// RUN: %clangxx -std=c++17 -fsanitize=function %s -O3 -g -o %t // RUN: %run %t 2>&1 | FileCheck %s // Verify that we can disable symbolization if needed: // RUN: %env_ubsan_opts=symbolize=0 %run %t 2>&1 | FileCheck %s --check-prefix=NOSYM @@ -23,9 +23,49 @@ reinterpret_cast(reinterpret_cast(f))(42); } +void f1(int) {} +void f2(unsigned int) {} +void f3(int) noexcept {} +void f4(unsigned int) noexcept {} + +void check_noexcept_calls() { + void (*p1)(int); + p1 = &f1; + p1(0); + p1 = reinterpret_cast(&f2); + // CHECK: function.cpp:[[@LINE+2]]:3: runtime error: call to function f2(unsigned int) through pointer to incorrect function type 'void (*)(int)' + // NOSYM: function.cpp:[[@LINE+1]]:3: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)(int)' + p1(0); + p1 = &f3; + p1(0); + p1 = reinterpret_cast(&f4); + // CHECK: function.cpp:[[@LINE+2]]:3: runtime error: call to function f4(unsigned int) through pointer to incorrect function type 'void (*)(int)' + // NOSYM: function.cpp:[[@LINE+1]]:3: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)(int)' + p1(0); + + void (*p2)(int) noexcept; + p2 = reinterpret_cast(&f1); + // TODO: Unclear whether calling a non-noexcept function through a pointer to + // nexcept function should cause an error. + // CHECK-NOT: function.cpp:[[@LINE+2]]:3: runtime error: call to function f1(int) through pointer to incorrect function type 'void (*)(int) noexcept' + // NOSYM-NOT: function.cpp:[[@LINE+1]]:3: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)(int) noexcept' + p2(0); + p2 = reinterpret_cast(&f2); + // CHECK: function.cpp:[[@LINE+2]]:3: runtime error: call to function f2(unsigned int) through pointer to incorrect function type 'void (*)(int) noexcept' + // NOSYM: function.cpp:[[@LINE+1]]:3: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)(int) noexcept' + p2(0); + p2 = &f3; + p2(0); + p2 = reinterpret_cast(&f4); + // CHECK: function.cpp:[[@LINE+2]]:3: runtime error: call to function f4(unsigned int) through pointer to incorrect function type 'void (*)(int) noexcept' + // NOSYM: function.cpp:[[@LINE+1]]:3: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)(int) noexcept' + p2(0); +} + int main(void) { make_valid_call(); make_invalid_call(); + check_noexcept_calls(); // Check that no more errors will be printed. // CHECK-NOT: runtime error: call to function // NOSYM-NOT: runtime error: call to function