summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephan Bergmann <sbergman@redhat.com>2017-12-18 13:05:41 +0000
committerStephan Bergmann <sbergman@redhat.com>2017-12-18 13:05:41 +0000
commit4b787a08f59576b523733b556200287ea54fdc05 (patch)
treed6b7520cdf956c9dd162bc5459191034ba05a53f
parent0fb53ba1a95663a10966d45dddb41fe8bea9d9aa (diff)
No -fsanitize=function warning when calling noexcept function through non-noexcept pointer in C++17
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 be the only one that happens to be used on platforms that support -fsanitize=function, and 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". This is the compiler-rt part of a patch covering both cfe and compiler-rt. Differential Revision: https://reviews.llvm.org/D40720 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@320977 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/ubsan/ubsan_handlers.cc56
-rw-r--r--lib/ubsan/ubsan_handlers.h3
-rw-r--r--test/ubsan/TestCases/TypeCheck/Function/function.cpp40
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