chrisdangelo updated this revision to Diff 395734.
chrisdangelo added a comment.

This change includes clang-format edits.


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

https://reviews.llvm.org/D113530

Files:
  clang/include/clang/Basic/Attr.td
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  clang/test/Analysis/malloc-annotations.c

Index: clang/test/Analysis/malloc-annotations.c
===================================================================
--- clang/test/Analysis/malloc-annotations.c
+++ clang/test/Analysis/malloc-annotations.c
@@ -16,6 +16,10 @@
        __attribute((ownership_holds(malloc, 1, 2)));
 void __attribute((ownership_returns(malloc, 1))) *my_malloc2(size_t);
 void __attribute((ownership_holds(malloc, 1))) my_hold(void *);
+void my_free_first_parameter(void *__attribute__((ownership_takes(malloc))));
+void my_free_second_parameter(void *, void *__attribute__((ownership_takes(malloc))));
+void my_free_two_parameters(void *__attribute__((ownership_takes(malloc))), void *__attribute__((ownership_takes(malloc))));
+void __attribute((ownership_takes(malloc, 1))) my_free_both_params_via_param_and_func(void *, void *__attribute__((ownership_takes(malloc))));
 
 // Duplicate attributes are silly, but not an error.
 // Duplicate attribute has no extra effect.
@@ -273,3 +277,49 @@
   my_freeBoth(p, q);
 }
 
+int *testFreeFirstParameterUseAfterFree() {
+  int *p = malloc(4);
+
+  my_free_first_parameter(p);
+  return p; // expected-warning{{Use of memory after it is freed}}
+}
+
+int *testFreeSecondParameterUseAfterFree() {
+  int *p1 = malloc(4);
+  int *p2 = malloc(4);
+
+  my_free_second_parameter(p1, p2);
+  return p2; // expected-warning{{Use of memory after it is freed}}
+}
+
+int *testFreeSecondParameterNoUseAfterFree() {
+  int *p1 = malloc(4);
+  int *p2 = malloc(4);
+
+  my_free_second_parameter(p1, p2);
+  return p1;
+}
+
+int *testFreeFirstOfTwoParametersUseAfterFree() {
+  int *p1 = malloc(4);
+  int *p2 = malloc(4);
+
+  my_free_two_parameters(p1, p2);
+  return p1; // expected-warning{{Use of memory after it is freed}}
+}
+
+int *testFreeSecondOfTwoParametersUseAfterFree() {
+  int *p1 = malloc(4);
+  int *p2 = malloc(4);
+
+  my_free_two_parameters(p1, p2);
+  return p2; // expected-warning{{Use of memory after it is freed}}
+}
+
+int *testAuthorMayUseFuncDeclAndParamOwnershipAttr() {
+  int *p1 = malloc(4);
+  int *p2 = malloc(4);
+
+  my_free_both_params_via_param_and_func(p1, p2);
+  return p2; // expected-warning{{Use of memory after it is freed}}
+}
Index: clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -301,11 +301,7 @@
                      check::NewAllocator, check::PostStmt<BlockExpr>,
                      check::PostObjCMessage, check::Location, eval::Assume> {
 public:
-  /// In pessimistic mode, the checker assumes that it does not know which
-  /// functions might free the memory.
-  /// In optimistic mode, the checker assumes that all user-defined functions
-  /// which might free a pointer are annotated.
-  DefaultBool ShouldIncludeOwnershipAnnotatedFunctions;
+  DefaultBool IsOptimisticAnalyzerOptionEnabled;
 
   DefaultBool ShouldRegisterNoOwnershipChangeVisitor;
 
@@ -448,6 +444,16 @@
   /// if the macro value could be determined, and if yes the value itself.
   mutable Optional<KernelZeroSizePtrValueTy> KernelZeroSizePtrValue;
 
+  LLVM_NODISCARD
+  ProgramStateRef ownershipFuncAttr(const CallEvent &Call, CheckerContext &C,
+                                    const FunctionDecl *FD,
+                                    ProgramStateRef S) const;
+
+  LLVM_NODISCARD
+  ProgramStateRef ownershipParamAttr(const CallEvent &Call, CheckerContext &C,
+                                     const FunctionDecl *FD,
+                                     ProgramStateRef S) const;
+
   /// Process C++ operator new()'s allocation, which is the part of C++
   /// new-expression that goes before the constructor.
   LLVM_NODISCARD
@@ -1080,7 +1086,7 @@
       ReallocatingMemFnMap.lookup(Call))
     return true;
 
-  if (!ShouldIncludeOwnershipAnnotatedFunctions)
+  if (!IsOptimisticAnalyzerOptionEnabled)
     return false;
 
   const auto *Func = dyn_cast<FunctionDecl>(Call.getDecl());
@@ -1396,33 +1402,109 @@
   C.addTransition(State);
 }
 
-void MallocChecker::checkOwnershipAttr(const CallEvent &Call,
-                                       CheckerContext &C) const {
+static const FunctionDecl *functionDeclForCall(const CallEvent &Call,
+                                               CheckerContext &C) {
   ProgramStateRef State = C.getState();
   const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
   if (!CE)
-    return;
+    return nullptr;
+
   const FunctionDecl *FD = C.getCalleeDecl(CE);
   if (!FD)
-    return;
-  if (ShouldIncludeOwnershipAnnotatedFunctions ||
-      ChecksEnabled[CK_MismatchedDeallocatorChecker]) {
-    // Check all the attributes, if there are any.
-    // There can be multiple of these attributes.
-    if (FD->hasAttrs())
-      for (const auto *I : FD->specific_attrs<OwnershipAttr>()) {
-        switch (I->getOwnKind()) {
-        case OwnershipAttr::Returns:
-          State = MallocMemReturnsAttr(C, Call, I, State);
-          break;
-        case OwnershipAttr::Takes:
-        case OwnershipAttr::Holds:
-          State = FreeMemAttr(C, Call, I, State);
-          break;
-        }
+    return nullptr;
+
+  return FD;
+}
+
+ProgramStateRef MallocChecker::ownershipFuncAttr(const CallEvent &Call,
+                                                 CheckerContext &C,
+                                                 const FunctionDecl *FD,
+                                                 ProgramStateRef S) const {
+  if (!FD->hasAttrs())
+    return S;
+
+  for (const auto *I : FD->specific_attrs<OwnershipAttr>()) {
+    ProgramStateRef AttrS = nullptr;
+
+    switch (I->getOwnKind()) {
+    case OwnershipAttr::Returns:
+      AttrS = MallocMemReturnsAttr(C, Call, I, S);
+      break;
+    case OwnershipAttr::Takes:
+    case OwnershipAttr::Holds:
+      AttrS = FreeMemAttr(C, Call, I, S);
+      break;
+    }
+
+    if (AttrS)
+      S = AttrS;
+  }
+
+  return S;
+}
+
+ProgramStateRef MallocChecker::ownershipParamAttr(const CallEvent &Call,
+                                                  CheckerContext &C,
+                                                  const FunctionDecl *FD,
+                                                  ProgramStateRef S) const {
+  for (const auto *P : FD->parameters()) {
+    unsigned int Index = P->getFunctionScopeIndex();
+    bool IsParamInBounds = Index >= 0 && Index < Call.getNumArgs();
+    if (!IsParamInBounds)
+      continue;
+
+    if (!P->hasAttrs())
+      continue;
+
+    for (const auto *I : P->specific_attrs<OwnershipAttr>()) {
+      if (I->getModule()->getName() != "malloc")
+        continue;
+
+      bool IsAlloc = false;
+      bool Holds = false;
+      ProgramStateRef AttrS = nullptr;
+      const Expr *SizeEx = Call.getArgExpr(Index);
+      const SVal Init = UndefinedVal();
+
+      switch (I->getOwnKind()) {
+      case OwnershipAttr::Returns:
+        AttrS = MallocMemAux(C, Call, SizeEx, Init, S, AF_Malloc);
+        break;
+      case OwnershipAttr::Holds:
+        Holds = true;
+        LLVM_FALLTHROUGH;
+      case OwnershipAttr::Takes:
+        AttrS = FreeMemAux(C, Call, S, Index, Holds, IsAlloc, AF_Malloc);
+        break;
       }
+
+      if (AttrS)
+        S = AttrS;
+    }
   }
-  C.addTransition(State);
+
+  return S;
+}
+
+void MallocChecker::checkOwnershipAttr(const CallEvent &Call,
+                                       CheckerContext &C) const {
+  bool IsEnabled = IsOptimisticAnalyzerOptionEnabled ||
+                   ChecksEnabled[CK_MismatchedDeallocatorChecker];
+  if (!IsEnabled)
+    return;
+
+  ProgramStateRef S = C.getState();
+  if (!S)
+    return;
+
+  const FunctionDecl *FD = functionDeclForCall(Call, C);
+  if (!FD)
+    return;
+
+  S = ownershipFuncAttr(Call, C, FD, S);
+  S = ownershipParamAttr(Call, C, FD, S);
+
+  C.addTransition(S);
 }
 
 void MallocChecker::checkPostCall(const CallEvent &Call,
@@ -3562,7 +3644,7 @@
 
 void ento::registerDynamicMemoryModeling(CheckerManager &mgr) {
   auto *checker = mgr.registerChecker<MallocChecker>();
-  checker->ShouldIncludeOwnershipAnnotatedFunctions =
+  checker->IsOptimisticAnalyzerOptionEnabled =
       mgr.getAnalyzerOptions().getCheckerBooleanOption(checker, "Optimistic");
   checker->ShouldRegisterNoOwnershipChangeVisitor =
       mgr.getAnalyzerOptions().getCheckerBooleanOption(
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -1714,7 +1714,50 @@
   return false;
 }
 
-static void handleOwnershipAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+static IdentifierInfo *handleOwnershipAttrModule(Sema &S,
+                                                 const ParsedAttr &AL) {
+  if (!AL.isArgIdent(0)) {
+    S.Diag(AL.getLoc(), diag::err_attribute_argument_n_type)
+        << AL << 1 << AANT_ArgumentIdentifier;
+    return nullptr;
+  }
+
+  IdentifierInfo *Module = AL.getArgAsIdent(0)->Ident;
+  StringRef ModuleName = Module->getName();
+  if (normalizeName(ModuleName)) {
+    Module = &S.PP.getIdentifierTable().get(ModuleName);
+  }
+
+  return Module;
+}
+
+static void handleOwnershipParamAttr(Sema &S, ParmVarDecl *D,
+                                     const ParsedAttr &AL) {
+  IdentifierInfo *M = handleOwnershipAttrModule(S, AL);
+  if (!M)
+    return;
+
+  if (AL.getNumArgs() > 1) {
+    S.Diag(AL.getLoc(), diag::err_attribute_too_many_arguments) << AL << 1;
+    return;
+  }
+
+  QualType QT = D->getType();
+  if (!S.isValidPointerAttrType(QT)) {
+    SourceRange AttrParmRange = SourceRange();
+    SourceRange TypeRange = D->getSourceRange();
+    S.Diag(AL.getLoc(), diag::warn_attribute_pointers_only)
+        << AL << AttrParmRange << TypeRange << 0;
+    return;
+  }
+
+  ParamIdx *Start = nullptr;
+  unsigned Size = 0;
+  llvm::array_pod_sort(Start, Start + Size);
+  D->addAttr(::new (S.Context) OwnershipAttr(S.Context, AL, M, Start, Size));
+}
+
+static void handleOwnershipFuncAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   // This attribute must be applied to a function declaration. The first
   // argument to the attribute must be an identifier, the name of the resource,
   // for example: malloc. The following arguments must be argument indexes, the
@@ -1723,11 +1766,9 @@
   // after being held. free() should be __attribute((ownership_takes)), whereas
   // a list append function may well be __attribute((ownership_holds)).
 
-  if (!AL.isArgIdent(0)) {
-    S.Diag(AL.getLoc(), diag::err_attribute_argument_n_type)
-        << AL << 1 << AANT_ArgumentIdentifier;
+  IdentifierInfo *Module = handleOwnershipAttrModule(S, AL);
+  if (!Module)
     return;
-  }
 
   // Figure out our Kind.
   OwnershipAttr::OwnershipKind K =
@@ -1750,13 +1791,6 @@
     break;
   }
 
-  IdentifierInfo *Module = AL.getArgAsIdent(0)->Ident;
-
-  StringRef ModuleName = Module->getName();
-  if (normalizeName(ModuleName)) {
-    Module = &S.PP.getIdentifierTable().get(ModuleName);
-  }
-
   SmallVector<ParamIdx, 8> OwnershipArgs;
   for (unsigned i = 1; i < AL.getNumArgs(); ++i) {
     Expr *Ex = AL.getArgAsExpr(i);
@@ -8105,7 +8139,10 @@
     handleAllocAlignAttr(S, D, AL);
     break;
   case ParsedAttr::AT_Ownership:
-    handleOwnershipAttr(S, D, AL);
+    if (auto *PVD = dyn_cast<ParmVarDecl>(D))
+      handleOwnershipParamAttr(S, PVD, AL);
+    else
+      handleOwnershipFuncAttr(S, D, AL);
     break;
   case ParsedAttr::AT_Naked:
     handleNakedAttr(S, D, AL);
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -2255,7 +2255,7 @@
   }];
   let Args = [IdentifierArgument<"Module">,
               VariadicParamIdxArgument<"Args">];
-  let Subjects = SubjectList<[HasFunctionProto]>;
+  let Subjects = SubjectList<[HasFunctionProto, ParmVar]>;
   let Documentation = [Undocumented];
 }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to