[compiler-rt] r320977 - No -fsanitize=function warning when calling noexcept function through non-noexcept pointer in C++17

Stephan Bergmann via llvm-commits llvm-commits at lists.llvm.org
Mon Dec 18 05:05:41 PST 2017


Author: sberg
Date: Mon Dec 18 05:05:41 2017
New Revision: 320977

URL: http://llvm.org/viewvc/llvm-project?rev=320977&view=rev
Log:
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

Modified:
    compiler-rt/trunk/lib/ubsan/ubsan_handlers.cc
    compiler-rt/trunk/lib/ubsan/ubsan_handlers.h
    compiler-rt/trunk/test/ubsan/TestCases/TypeCheck/Function/function.cpp

Modified: compiler-rt/trunk/lib/ubsan/ubsan_handlers.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/ubsan/ubsan_handlers.cc?rev=320977&r1=320976&r2=320977&view=diff
==============================================================================
--- compiler-rt/trunk/lib/ubsan/ubsan_handlers.cc (original)
+++ compiler-rt/trunk/lib/ubsan/ubsan_handlers.cc Mon Dec 18 05:05:41 2017
@@ -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_bui
   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(F
        "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,

Modified: compiler-rt/trunk/lib/ubsan/ubsan_handlers.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/ubsan/ubsan_handlers.h?rev=320977&r1=320976&r2=320977&view=diff
==============================================================================
--- compiler-rt/trunk/lib/ubsan/ubsan_handlers.h (original)
+++ compiler-rt/trunk/lib/ubsan/ubsan_handlers.h Mon Dec 18 05:05:41 2017
@@ -140,11 +140,12 @@ RECOVERABLE(invalid_builtin, InvalidBuil
 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;

Modified: compiler-rt/trunk/test/ubsan/TestCases/TypeCheck/Function/function.cpp
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/ubsan/TestCases/TypeCheck/Function/function.cpp?rev=320977&r1=320976&r2=320977&view=diff
==============================================================================
--- compiler-rt/trunk/test/ubsan/TestCases/TypeCheck/Function/function.cpp (original)
+++ compiler-rt/trunk/test/ubsan/TestCases/TypeCheck/Function/function.cpp Mon Dec 18 05:05:41 2017
@@ -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




More information about the llvm-commits mailing list