sberg created this revision. Herald added a subscriber: kubamracek. As discussed in the mail thread https://groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/T64_dW3WKUk "Calling noexcept function throug non-noexcept pointer is undefined behavior?", such a call should not be UB. However, Clang currently warns about it.
There is no cheap check whether two function type_infos only differ in noexcept, so pass those two type_infos as additional data to the function_type_mismatch handler (with the optimization of passing a null "static callee type" info when that is already noexcept, so the additional check can be avoided anyway). For the Itanium ABI (which appears to only record noexcept information for pointer-to-function type_infos, not for function type_infos themselves), we then need to check the mangled names for occurrence of "Do" representing "noexcept". https://reviews.llvm.org/D40720 Files: clang/lib/CodeGen/CGExpr.cpp compiler-rt/lib/ubsan/ubsan_handlers.cc compiler-rt/lib/ubsan/ubsan_handlers.h compiler-rt/test/ubsan/TestCases/TypeCheck/Function/function.cpp
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<void (*)(int)>(reinterpret_cast<uintptr_t>(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<void (*)(int)>(&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<void (*)(int)>(&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<void (*)(int) noexcept>(&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<void (*)(int) noexcept>(&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<void (*)(int) noexcept>(&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 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 <cstring> +#include <typeinfo> + 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 <CV-qualifiers>. + if (*Mangled1 == 'V') { + if (*Mangled2 != 'V') + return false; + ++Mangled1; + ++Mangled2; + } + if (*Mangled1 == 'K') { + if (*Mangled2 != 'K') + return false; + ++Mangled1; + ++Mangled2; + } + + // Check for "Do" <exception-spec>. + 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<std::type_info *>(Data->NonNoexceptRTTI), + reinterpret_cast<std::type_info *>(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: 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<FunctionProtoType>(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);
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits