diff options
-rw-r--r-- | lib/ubsan/ubsan_handlers.cc | 56 | ||||
-rw-r--r-- | lib/ubsan/ubsan_handlers.h | 3 | ||||
-rw-r--r-- | test/ubsan/TestCases/TypeCheck/Function/function.cpp | 40 |
3 files changed, 89 insertions, 10 deletions
diff --git a/lib/ubsan/ubsan_handlers.cc b/lib/ubsan/ubsan_handlers.cc index 1112ce1cc..064d9b78c 100644 --- a/lib/ubsan/ubsan_handlers.cc +++ b/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; @@ -461,14 +464,50 @@ void __ubsan::__ubsan_handle_invalid_builtin_abort(InvalidBuiltinData *Data) { 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); @@ -481,20 +520,21 @@ static void handleFunctionTypeMismatch(FunctionTypeMismatchData *Data, "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, diff --git a/lib/ubsan/ubsan_handlers.h b/lib/ubsan/ubsan_handlers.h index 311776b9f..4ec2592e3 100644 --- a/lib/ubsan/ubsan_handlers.h +++ b/lib/ubsan/ubsan_handlers.h @@ -140,11 +140,12 @@ RECOVERABLE(invalid_builtin, InvalidBuiltinData *Data) 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; diff --git a/test/ubsan/TestCases/TypeCheck/Function/function.cpp b/test/ubsan/TestCases/TypeCheck/Function/function.cpp index 25b2bdc32..409eeb2ff 100644 --- a/test/ubsan/TestCases/TypeCheck/Function/function.cpp +++ b/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 @@ void make_invalid_call() { 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 |