]> granicus.if.org Git - clang/commitdiff
[analyzer] Fix yet-another-crash in body-farming std::call_once
authorGeorge Karpenkov <ekarpenkov@apple.com>
Fri, 2 Feb 2018 01:44:07 +0000 (01:44 +0000)
committerGeorge Karpenkov <ekarpenkov@apple.com>
Fri, 2 Feb 2018 01:44:07 +0000 (01:44 +0000)
Crash occurs when parameters to the callback and to std::call_once
mismatch, and C++ is supposed to auto-construct an argument.

Filed by Alexander Kornienko in
https://bugs.llvm.org/show_bug.cgi?id=36149

rdar://37034403

Differential Revision: https://reviews.llvm.org/D42777

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@324046 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Analysis/BodyFarm.cpp
test/Analysis/call_once.cpp

index 492d01f6c98e86f143bd42e3e4bec1defcfb457d..f4bea4abf14ec1333bd6a49fdfb51c2fb26e4cb5 100644 (file)
@@ -406,6 +406,16 @@ static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) {
   // reference.
   for (unsigned int ParamIdx = 2; ParamIdx < D->getNumParams(); ParamIdx++) {
     const ParmVarDecl *PDecl = D->getParamDecl(ParamIdx);
+    if (PDecl &&
+        CallbackFunctionType->getParamType(ParamIdx - 2)
+                .getNonReferenceType()
+                .getCanonicalType() !=
+            PDecl->getType().getNonReferenceType().getCanonicalType()) {
+      DEBUG(llvm::dbgs() << "Types of params of the callback do not match "
+                         << "params passed to std::call_once, "
+                         << "ignoring the call\n");
+      return nullptr;
+    }
     Expr *ParamExpr = M.makeDeclRefExpr(PDecl);
     if (!CallbackFunctionType->getParamType(ParamIdx - 2)->isReferenceType()) {
       QualType PTy = PDecl->getType().getNonReferenceType();
@@ -816,4 +826,3 @@ Stmt *BodyFarm::getBody(const ObjCMethodDecl *D) {
 
   return Val.getValue();
 }
-
index 445c616b3cbbda4fb46dea7b6b710c5e75f911bd..dd4b2d45516cfbb42a6c73d0caac5aa6758c6b91 100644 (file)
@@ -9,9 +9,26 @@
 
 void clang_analyzer_eval(bool);
 
-// Faking std::std::call_once implementation.
+// Faking std::call_once implementation.
 namespace std {
 
+// Fake std::function implementation.
+template <typename>
+class function;
+class function_base {
+ public:
+  long field;
+};
+template <typename R, typename... P>
+class function<R(P...)> : function_base {
+ public:
+   R operator()(P...) const {
+
+     // Read from a super-class necessary to reproduce a crash.
+     bool a = field;
+   }
+};
+
 #ifndef EMULATE_LIBSTDCPP
 typedef struct once_flag_s {
   unsigned long __state_ = 0;
@@ -360,3 +377,29 @@ void test_implicit_funcptr() {
   clang_analyzer_eval(x == 42); // expected-warning{{TRUE}}
 #endif
 }
+
+int param_passed(int *x) {
+  return *x; // no-warning, as std::function is not working yet.
+}
+
+void callback_taking_func_ok(std::function<void(int*)> &innerCallback) {
+  innerCallback(nullptr);
+}
+
+// The provided callback expects an std::function, but instead a pointer
+// to a C++ function is provided.
+void callback_with_implicit_cast_ok() {
+  std::once_flag flag;
+  call_once(flag, callback_taking_func_ok, &param_passed);
+}
+
+void callback_taking_func(std::function<void()> &innerCallback) {
+  innerCallback();
+}
+
+// The provided callback expects an std::function, but instead a C function
+// name is provided, and C++ implicitly auto-constructs a pointer from it.
+void callback_with_implicit_cast() {
+  std::once_flag flag;
+  call_once(flag, callback_taking_func, callback_with_implicit_cast);
+}