This revision was automatically updated to reflect the committed changes.
Closed by commit rG425a83a5f069: [Sema] adds basic -Wfree-nonheap-object 
functionality (authored by cjdb, committed by george.burgess.iv).
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D89988/new/

https://reviews.llvm.org/D89988

Files:
  clang/include/clang/Basic/Builtins.def
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Sema/Sema.h
  clang/lib/AST/Decl.cpp
  clang/lib/Sema/SemaChecking.cpp
  clang/test/Sema/warn-free-nonheap-object.c
  clang/test/Sema/warn-free-nonheap-object.cpp

Index: clang/test/Sema/warn-free-nonheap-object.cpp
===================================================================
--- /dev/null
+++ clang/test/Sema/warn-free-nonheap-object.cpp
@@ -0,0 +1,90 @@
+// RUN: %clang_cc1 -Wfree-nonheap-object -std=c++11 -x c++ -fsyntax-only -verify %s
+
+extern "C" void free(void *) {}
+
+namespace std {
+using size_t = decltype(sizeof(0));
+void *malloc(size_t);
+void free(void *p);
+} // namespace std
+
+int GI;
+
+struct S {
+  operator char *() { return ptr; }
+
+private:
+  char *ptr = (char *)std::malloc(10);
+};
+
+void test1() {
+  {
+    free(&GI); // expected-warning {{attempt to call free on non-heap object 'GI'}}
+  }
+  {
+    static int SI = 0;
+    free(&SI); // expected-warning {{attempt to call free on non-heap object 'SI'}}
+  }
+  {
+    int I = 0;
+    free(&I); // expected-warning {{attempt to call free on non-heap object 'I'}}
+  }
+  {
+    int I = 0;
+    int *P = &I;
+    free(P);
+  }
+  {
+    void *P = std::malloc(8);
+    free(P); // FIXME diagnosing this would require control flow analysis.
+  }
+  {
+    int A[] = {0, 1, 2, 3};
+    free(A); // expected-warning {{attempt to call free on non-heap object 'A'}}
+  }
+  {
+    int A[] = {0, 1, 2, 3};
+    free(&A); // expected-warning {{attempt to call free on non-heap object 'A'}}
+  }
+  {
+    S s;
+    free(s);
+    free(&s); // expected-warning {{attempt to call free on non-heap object 's'}}
+  }
+}
+
+void test2() {
+  {
+    std::free(&GI); // expected-warning {{attempt to call std::free on non-heap object 'GI'}}
+  }
+  {
+    static int SI = 0;
+    std::free(&SI); // expected-warning {{attempt to call std::free on non-heap object 'SI'}}
+  }
+  {
+    int I = 0;
+    std::free(&I); // expected-warning {{attempt to call std::free on non-heap object 'I'}}
+  }
+  {
+    int I = 0;
+    int *P = &I;
+    std::free(P); // FIXME diagnosing this would require control flow analysis.
+  }
+  {
+    void *P = std::malloc(8);
+    std::free(P);
+  }
+  {
+    int A[] = {0, 1, 2, 3};
+    std::free(A); // expected-warning {{attempt to call std::free on non-heap object 'A'}}
+  }
+  {
+    int A[] = {0, 1, 2, 3};
+    std::free(&A); // expected-warning {{attempt to call std::free on non-heap object 'A'}}
+  }
+  {
+    S s;
+    std::free(s);
+    std::free(&s); // expected-warning {{attempt to call std::free on non-heap object 's'}}
+  }
+}
Index: clang/test/Sema/warn-free-nonheap-object.c
===================================================================
--- /dev/null
+++ clang/test/Sema/warn-free-nonheap-object.c
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -Wfree-nonheap-object -fsyntax-only -verify %s
+
+typedef __SIZE_TYPE__ size_t;
+void *malloc(size_t);
+void free(void *);
+
+int GI;
+void test() {
+  {
+    free(&GI); // expected-warning {{attempt to call free on non-heap object 'GI'}}
+  }
+  {
+    static int SI = 0;
+    free(&SI); // expected-warning {{attempt to call free on non-heap object 'SI'}}
+  }
+  {
+    int I = 0;
+    free(&I); // expected-warning {{attempt to call free on non-heap object 'I'}}
+  }
+  {
+    int I = 0;
+    int *P = &I;
+    free(P); // FIXME diagnosing this would require control flow analysis.
+  }
+  {
+    void *P = malloc(8);
+    free(P);
+  }
+  {
+    int A[] = {0, 1, 2, 3};
+    free(A);  // expected-warning {{attempt to call free on non-heap object 'A'}}
+    free(&A); // expected-warning {{attempt to call free on non-heap object 'A'}}
+  }
+}
Index: clang/lib/Sema/SemaChecking.cpp
===================================================================
--- clang/lib/Sema/SemaChecking.cpp
+++ clang/lib/Sema/SemaChecking.cpp
@@ -4496,16 +4496,24 @@
     DiagnoseCStringFormatDirectiveInCFAPI(*this, FDecl, Args, NumArgs);
 
   unsigned CMId = FDecl->getMemoryFunctionKind();
-  if (CMId == 0)
-    return false;
 
   // Handle memory setting and copying functions.
-  if (CMId == Builtin::BIstrlcpy || CMId == Builtin::BIstrlcat)
+  switch (CMId) {
+  case 0:
+    return false;
+  case Builtin::BIstrlcpy: // fallthrough
+  case Builtin::BIstrlcat:
     CheckStrlcpycatArguments(TheCall, FnInfo);
-  else if (CMId == Builtin::BIstrncat)
+    break;
+  case Builtin::BIstrncat:
     CheckStrncatArguments(TheCall, FnInfo);
-  else
+    break;
+  case Builtin::BIfree:
+    CheckFreeArguments(TheCall);
+    break;
+  default:
     CheckMemaccessArguments(TheCall, CMId, FnInfo);
+  }
 
   return false;
 }
@@ -10098,6 +10106,57 @@
     << FixItHint::CreateReplacement(SR, OS.str());
 }
 
+namespace {
+void CheckFreeArgumentsAddressof(Sema &S, const std::string &CalleeName,
+                                 const UnaryOperator *UnaryExpr) {
+  if (UnaryExpr->getOpcode() != UnaryOperator::Opcode::UO_AddrOf)
+    return;
+
+  const auto *Lvalue = dyn_cast<DeclRefExpr>(UnaryExpr->getSubExpr());
+  if (Lvalue == nullptr)
+    return;
+
+  const auto *Var = dyn_cast<VarDecl>(Lvalue->getDecl());
+  if (Var == nullptr)
+    return;
+
+  StorageClass Class = Var->getStorageClass();
+  if (Class == StorageClass::SC_Extern ||
+      Class == StorageClass::SC_PrivateExtern ||
+      Var->getType()->isReferenceType())
+    return;
+
+  S.Diag(UnaryExpr->getBeginLoc(), diag::warn_free_nonheap_object)
+      << CalleeName << Var;
+}
+
+void CheckFreeArgumentsStackArray(Sema &S, const std::string &CalleeName,
+                                  const DeclRefExpr *Lvalue) {
+  if (!Lvalue->getType()->isArrayType())
+    return;
+
+  const auto *Var = dyn_cast<VarDecl>(Lvalue->getDecl());
+  if (Var == nullptr)
+    return;
+
+  S.Diag(Lvalue->getBeginLoc(), diag::warn_free_nonheap_object)
+      << CalleeName << Var;
+}
+} // namespace
+
+/// Alerts the user that they are attempting to free a non-malloc'd object.
+void Sema::CheckFreeArguments(const CallExpr *E) {
+  const Expr *Arg = E->getArg(0)->IgnoreParenCasts();
+  const std::string CalleeName =
+      dyn_cast<FunctionDecl>(E->getCalleeDecl())->getQualifiedNameAsString();
+
+  if (const auto *UnaryExpr = dyn_cast<UnaryOperator>(Arg))
+    return CheckFreeArgumentsAddressof(*this, CalleeName, UnaryExpr);
+
+  if (const auto *Lvalue = dyn_cast<DeclRefExpr>(Arg))
+    return CheckFreeArgumentsStackArray(*this, CalleeName, Lvalue);
+}
+
 void
 Sema::CheckReturnValExpr(Expr *RetValExp, QualType lhsType,
                          SourceLocation ReturnLoc,
Index: clang/lib/AST/Decl.cpp
===================================================================
--- clang/lib/AST/Decl.cpp
+++ clang/lib/AST/Decl.cpp
@@ -3959,6 +3959,9 @@
   case Builtin::BIbzero:
     return Builtin::BIbzero;
 
+  case Builtin::BIfree:
+    return Builtin::BIfree;
+
   default:
     if (isExternC()) {
       if (FnInfo->isStr("memset"))
@@ -3987,6 +3990,9 @@
         return Builtin::BIstrlen;
       else if (FnInfo->isStr("bzero"))
         return Builtin::BIbzero;
+    } else if (isInStdNamespace()) {
+      if (FnInfo->isStr("free"))
+        return Builtin::BIfree;
     }
     break;
   }
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -12400,6 +12400,8 @@
   void CheckStrncatArguments(const CallExpr *Call,
                              IdentifierInfo *FnName);
 
+  void CheckFreeArguments(const CallExpr *E);
+
   void CheckReturnValExpr(Expr *RetValExp, QualType lhsType,
                           SourceLocation ReturnLoc,
                           bool isObjCMethod = false,
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7539,6 +7539,11 @@
 def warn_condition_is_assignment : Warning<"using the result of an "
   "assignment as a condition without parentheses">,
   InGroup<Parentheses>;
+def warn_free_nonheap_object
+  : Warning<"attempt to call %0 on non-heap object %1">,
+    InGroup<DiagGroup<"free-nonheap-object">>,
+    DefaultIgnore; // FIXME: add to -Wall after sufficient testing
+
 // Completely identical except off by default.
 def warn_condition_is_idiomatic_assignment : Warning<"using the result "
   "of an assignment as a condition without parentheses">,
Index: clang/include/clang/Basic/Builtins.def
===================================================================
--- clang/include/clang/Basic/Builtins.def
+++ clang/include/clang/Basic/Builtins.def
@@ -913,6 +913,7 @@
 LIBBUILTIN(_Exit, "vi",           "fr",    "stdlib.h", ALL_LANGUAGES)
 LIBBUILTIN(malloc, "v*z",         "f",     "stdlib.h", ALL_LANGUAGES)
 LIBBUILTIN(realloc, "v*v*z",      "f",     "stdlib.h", ALL_LANGUAGES)
+LIBBUILTIN(free,    "vv*",        "f",     "stdlib.h", ALL_LANGUAGES)
 LIBBUILTIN(strtod, "dcC*c**",     "f",     "stdlib.h", ALL_LANGUAGES)
 LIBBUILTIN(strtof, "fcC*c**",     "f",     "stdlib.h", ALL_LANGUAGES)
 LIBBUILTIN(strtold, "LdcC*c**",   "f",     "stdlib.h", ALL_LANGUAGES)
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to