Index: clang/lib/CodeGen/CGExpr.cpp =================================================================== --- clang/lib/CodeGen/CGExpr.cpp +++ clang/lib/CodeGen/CGExpr.cpp @@ -4465,10 +4465,14 @@ Builder.CreateICmpEQ(CalleeRTTI, FTRTTIConst); llvm::Constant *StaticData[] = { EmitCheckSourceLocation(E->getLocStart()), - EmitCheckTypeDescriptor(CalleeType) + EmitCheckTypeDescriptor(CalleeType), + cast(FnType)->isNothrow(getContext()) + ? llvm::Constant::getNullValue(FTRTTIConst->getType()) + : FTRTTIConst }; EmitCheck(std::make_pair(CalleeRTTIMatch, SanitizerKind::Function), - SanitizerHandler::FunctionTypeMismatch, StaticData, CalleePtr); + SanitizerHandler::FunctionTypeMismatch, StaticData, + {CalleePtr, CalleeRTTI}); Builder.CreateBr(Cont); EmitBlock(Cont); Index: compiler-rt/lib/ubsan/ubsan_handlers.h =================================================================== --- compiler-rt/lib/ubsan/ubsan_handlers.h +++ compiler-rt/lib/ubsan/ubsan_handlers.h @@ -140,11 +140,12 @@ struct FunctionTypeMismatchData { SourceLocation Loc; const TypeDescriptor &Type; + ValueHandle NonNoexceptRTTI; }; RECOVERABLE(function_type_mismatch, FunctionTypeMismatchData *Data, - ValueHandle Val) + ValueHandle Val, ValueHandle RTTI) struct NonNullReturnData { SourceLocation AttrLoc; Index: compiler-rt/lib/ubsan/ubsan_handlers.cc =================================================================== --- compiler-rt/lib/ubsan/ubsan_handlers.cc +++ compiler-rt/lib/ubsan/ubsan_handlers.cc @@ -18,6 +18,9 @@ #include "sanitizer_common/sanitizer_common.h" +#include +#include + using namespace __sanitizer; using namespace __ubsan; @@ -462,14 +465,50 @@ Die(); } -static void handleFunctionTypeMismatch(FunctionTypeMismatchData *Data, - ValueHandle Function, +// Check that TI2 represents the same function type as TI1, except that TI2 has +// "noexcept" and TI1 does not. +static bool checkForAddedNoexcept(const std::type_info *TI1, + const std::type_info *TI2) { + const char *Mangled1 = TI1->name(); + const char *Mangled2 = TI2->name(); + + // Skip . + if (*Mangled1 == 'V') { + if (*Mangled2 != 'V') + return false; + ++Mangled1; + ++Mangled2; + } + if (*Mangled1 == 'K') { + if (*Mangled2 != 'K') + return false; + ++Mangled1; + ++Mangled2; + } + + // Check for "Do" . + if (*Mangled2++ != 'D' || *Mangled2++ != 'o') + return false; + + // Check remainder is identical. + return std::strcmp(Mangled1, Mangled2) == 0; +} + +static bool handleFunctionTypeMismatch(FunctionTypeMismatchData *Data, + ValueHandle Function, ValueHandle RTTI, ReportOptions Opts) { + if (Data->NonNoexceptRTTI && + checkForAddedNoexcept( + reinterpret_cast(Data->NonNoexceptRTTI), + reinterpret_cast(RTTI))) { + return false; + } + SourceLocation CallLoc = Data->Loc.acquire(); ErrorType ET = ErrorType::FunctionTypeMismatch; if (ignoreReport(CallLoc, Opts, ET)) - return; + return true; ScopedReport R(Opts, CallLoc, ET); @@ -482,20 +521,21 @@ "call to function %0 through pointer to incorrect function type %1") << FName << Data->Type; Diag(FLoc, DL_Note, "%0 defined here") << FName; + return true; } void __ubsan::__ubsan_handle_function_type_mismatch(FunctionTypeMismatchData *Data, - ValueHandle Function) { + ValueHandle Function, ValueHandle RTTI) { GET_REPORT_OPTIONS(false); - handleFunctionTypeMismatch(Data, Function, Opts); + handleFunctionTypeMismatch(Data, Function, RTTI, Opts); } void __ubsan::__ubsan_handle_function_type_mismatch_abort( - FunctionTypeMismatchData *Data, ValueHandle Function) { + FunctionTypeMismatchData *Data, ValueHandle Function, ValueHandle RTTI) { GET_REPORT_OPTIONS(true); - handleFunctionTypeMismatch(Data, Function, Opts); - Die(); + if (handleFunctionTypeMismatch(Data, Function, RTTI, Opts)) + Die(); } static void handleNonNullReturn(NonNullReturnData *Data, SourceLocation *LocPtr, 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,47 @@ 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); + // CHECK: function.cpp:[[@LINE+2]]:3: runtime error: call to function f1(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 = 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