r311063 - [analyzer] Add support for reference counting of parameters on the callee side

Devin Coughlin via cfe-commits cfe-commits at lists.llvm.org
Wed Aug 16 21:19:07 PDT 2017


Author: dcoughlin
Date: Wed Aug 16 21:19:07 2017
New Revision: 311063

URL: http://llvm.org/viewvc/llvm-project?rev=311063&view=rev
Log:
[analyzer] Add support for reference counting of parameters on the callee side

This commit adds the functionality of performing reference counting on the
callee side for Integer Set Library (ISL) to Clang Static Analyzer's
RetainCountChecker.

Reference counting on the callee side can be extensively used to perform
debugging within a function (For example: Finding leaks on error paths).

Patch by Malhar Thakkar!

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

Modified:
    cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
    cfe/trunk/test/Analysis/retain-release-inline.m

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp?rev=311063&r1=311062&r2=311063&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp Wed Aug 16 21:19:07 2017
@@ -462,6 +462,7 @@ private:
   ArgEffect getDefaultArgEffect() const { return DefaultArgEffect; }
 
   friend class RetainSummaryManager;
+  friend class RetainCountChecker;
 };
 } // end anonymous namespace
 
@@ -1319,6 +1320,13 @@ static bool isTrustedReferenceCountImple
   return hasRCAnnotation(FD, "rc_ownership_trusted_implementation");
 }
 
+static bool isGeneralizedObjectRef(QualType Ty) {
+  if (Ty.getAsString().substr(0, 4) == "isl_")
+    return true;
+  else
+    return false;
+}
+
 //===----------------------------------------------------------------------===//
 // Summary creation for Selectors.
 //===----------------------------------------------------------------------===//
@@ -1848,6 +1856,15 @@ namespace {
 
   class CFRefLeakReport : public CFRefReport {
     const MemRegion* AllocBinding;
+    const Stmt *AllocStmt;
+
+    // Finds the function declaration where a leak warning for the parameter 'sym' should be raised.
+    void deriveParamLocation(CheckerContext &Ctx, SymbolRef sym);
+    // Finds the location where a leak warning for 'sym' should be raised.
+    void deriveAllocLocation(CheckerContext &Ctx, SymbolRef sym);
+    // Produces description of a leak warning which is printed on the console.
+    void createDescription(CheckerContext &Ctx, bool GCEnabled, bool IncludeAllocationLine);
+
   public:
     CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, bool GCEnabled,
                     const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym,
@@ -2427,13 +2444,25 @@ CFRefLeakReportVisitor::getEndPath(BugRe
   return llvm::make_unique<PathDiagnosticEventPiece>(L, os.str());
 }
 
-CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts,
-                                 bool GCEnabled, const SummaryLogTy &Log,
-                                 ExplodedNode *n, SymbolRef sym,
-                                 CheckerContext &Ctx,
-                                 bool IncludeAllocationLine)
-  : CFRefReport(D, LOpts, GCEnabled, Log, n, sym, false) {
+void CFRefLeakReport::deriveParamLocation(CheckerContext &Ctx, SymbolRef sym) {
+  const SourceManager& SMgr = Ctx.getSourceManager();
+
+  if (!sym->getOriginRegion())
+    return;
 
+  auto *Region = dyn_cast<DeclRegion>(sym->getOriginRegion());
+  if (Region) {
+    const Decl *PDecl = Region->getDecl();
+    if (PDecl && isa<ParmVarDecl>(PDecl)) {
+      PathDiagnosticLocation ParamLocation = PathDiagnosticLocation::create(PDecl, SMgr);
+      Location = ParamLocation;
+      UniqueingLocation = ParamLocation;
+      UniqueingDecl = Ctx.getLocationContext()->getDecl();
+    }
+  }
+}
+
+void CFRefLeakReport::deriveAllocLocation(CheckerContext &Ctx,SymbolRef sym) {
   // Most bug reports are cached at the location where they occurred.
   // With leaks, we want to unique them by the location where they were
   // allocated, and only report a single path.  To do this, we need to find
@@ -2457,8 +2486,12 @@ CFRefLeakReport::CFRefLeakReport(CFRefBu
   // FIXME: This will crash the analyzer if an allocation comes from an
   // implicit call (ex: a destructor call).
   // (Currently there are no such allocations in Cocoa, though.)
-  const Stmt *AllocStmt = PathDiagnosticLocation::getStmt(AllocNode);
-  assert(AllocStmt && "Cannot find allocation statement");
+  AllocStmt = PathDiagnosticLocation::getStmt(AllocNode);
+
+  if (!AllocStmt) {
+    AllocBinding = nullptr;
+    return;
+  }
 
   PathDiagnosticLocation AllocLocation =
     PathDiagnosticLocation::createBegin(AllocStmt, SMgr,
@@ -2469,8 +2502,10 @@ CFRefLeakReport::CFRefLeakReport(CFRefBu
   // leaks should be uniqued on the allocation site.
   UniqueingLocation = AllocLocation;
   UniqueingDecl = AllocNode->getLocationContext()->getDecl();
+}
 
-  // Fill in the description of the bug.
+void CFRefLeakReport::createDescription(CheckerContext &Ctx, bool GCEnabled, bool IncludeAllocationLine) {
+  assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid());
   Description.clear();
   llvm::raw_string_ostream os(Description);
   os << "Potential leak ";
@@ -2485,6 +2520,20 @@ CFRefLeakReport::CFRefLeakReport(CFRefBu
       os << " (allocated on line " << SL.getSpellingLineNumber() << ")";
     }
   }
+}
+
+CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts,
+                                 bool GCEnabled, const SummaryLogTy &Log,
+                                 ExplodedNode *n, SymbolRef sym,
+                                 CheckerContext &Ctx,
+                                 bool IncludeAllocationLine)
+  : CFRefReport(D, LOpts, GCEnabled, Log, n, sym, false) {
+
+  deriveAllocLocation(Ctx, sym);
+  if (!AllocBinding)
+    deriveParamLocation(Ctx, sym);
+
+  createDescription(Ctx, GCEnabled, IncludeAllocationLine);
 
   addVisitor(llvm::make_unique<CFRefLeakReportVisitor>(sym, GCEnabled, Log));
 }
@@ -2498,6 +2547,7 @@ class RetainCountChecker
   : public Checker< check::Bind,
                     check::DeadSymbols,
                     check::EndAnalysis,
+                    check::BeginFunction,
                     check::EndFunction,
                     check::PostStmt<BlockExpr>,
                     check::PostStmt<CastExpr>,
@@ -2682,6 +2732,7 @@ public:
                                 SymbolRef Sym, ProgramStateRef state) const;
 
   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
+  void checkBeginFunction(CheckerContext &C) const;
   void checkEndFunction(CheckerContext &C) const;
 
   ProgramStateRef updateSymbol(ProgramStateRef state, SymbolRef sym,
@@ -3903,6 +3954,36 @@ RetainCountChecker::processLeaks(Program
   return N;
 }
 
+void RetainCountChecker::checkBeginFunction(CheckerContext &Ctx) const {
+  if (!Ctx.inTopFrame())
+    return;
+
+  const LocationContext *LCtx = Ctx.getLocationContext();
+  const FunctionDecl *FD = dyn_cast<FunctionDecl>(LCtx->getDecl());
+
+  if (!FD || isTrustedReferenceCountImplementation(FD))
+    return;
+
+  ProgramStateRef state = Ctx.getState();
+
+  const RetainSummary *FunctionSummary = getSummaryManager(Ctx).getFunctionSummary(FD);
+  ArgEffects CalleeSideArgEffects = FunctionSummary->getArgEffects();
+
+  for (unsigned idx = 0, e = FD->getNumParams(); idx != e; ++idx) {
+    const ParmVarDecl *Param = FD->getParamDecl(idx);
+    SymbolRef Sym = state->getSVal(state->getRegion(Param, LCtx)).getAsSymbol();
+
+    QualType Ty = Param->getType();
+    const ArgEffect *AE = CalleeSideArgEffects.lookup(idx);
+    if (AE && *AE == DecRef && isGeneralizedObjectRef(Ty))
+      state = setRefBinding(state, Sym, RefVal::makeOwned(RetEffect::ObjKind::Generalized, Ty));
+    else if (isGeneralizedObjectRef(Ty))
+      state = setRefBinding(state, Sym, RefVal::makeNotOwned(RetEffect::ObjKind::Generalized, Ty));
+  }
+
+  Ctx.addTransition(state);
+}
+
 void RetainCountChecker::checkEndFunction(CheckerContext &Ctx) const {
   ProgramStateRef state = Ctx.getState();
   RefBindingsTy B = state->get<RefBindings>();

Modified: cfe/trunk/test/Analysis/retain-release-inline.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/retain-release-inline.m?rev=311063&r1=311062&r2=311063&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/retain-release-inline.m (original)
+++ cfe/trunk/test/Analysis/retain-release-inline.m Wed Aug 16 21:19:07 2017
@@ -302,6 +302,9 @@ void test_neg() {
 __attribute__((annotate("rc_ownership_returns_retained"))) isl_basic_map *isl_basic_map_cow(__attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap);
 void free(void *);
 
+void callee_side_parameter_checking_leak(__attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap) { // expected-warning {{Potential leak of an object}}
+}
+
 // As 'isl_basic_map_free' is annotated with 'rc_ownership_trusted_implementation', RetainCountChecker trusts its
 // implementation and doesn't analyze its body. If the annotation 'rc_ownership_trusted_implementation' is removed,
 // a leak warning is raised by RetainCountChecker as the analyzer is unable to detect a decrement in the reference
@@ -348,6 +351,37 @@ void test_leak_with_trusted_implementati
   isl_basic_map_free(bmap);
 }
 
+void callee_side_parameter_checking_incorrect_rc_decrement(isl_basic_map *bmap) {
+  isl_basic_map_free(bmap); // expected-warning {{Incorrect decrement of the reference count}}
+}
+
+__attribute__((annotate("rc_ownership_returns_retained"))) isl_basic_map *callee_side_parameter_checking_return_notowned_object(isl_basic_map *bmap) {
+  return bmap; // expected-warning {{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
+}
+
+__attribute__((annotate("rc_ownership_returns_retained"))) isl_basic_map *callee_side_parameter_checking_assign_consumed_parameter_leak_return(__attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap1, __attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap2) { // expected-warning {{Potential leak of an object}}
+  bmap1 = bmap2;
+  isl_basic_map_free(bmap2);
+  return bmap1;
+}
+
+__attribute__((annotate("rc_ownership_returns_retained"))) isl_basic_map *callee_side_parameter_checking_assign_consumed_parameter_leak(__attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap1, __attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap2) { // expected-warning {{Potential leak of an object}}
+  bmap1 = bmap2;
+  isl_basic_map_free(bmap1);
+  return bmap2;
+}
+
+__attribute__((annotate("rc_ownership_returns_retained"))) isl_basic_map *error_path_leak(__attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap1, __attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap2) { // expected-warning {{Potential leak of an object}}
+  bmap1 = isl_basic_map_cow(bmap1);
+  if (!bmap1 || !bmap2)
+    goto error;
+
+  isl_basic_map_free(bmap2);
+  return bmap1;
+error:
+  return isl_basic_map_free(bmap1);
+}
+
 //===----------------------------------------------------------------------===//
 // Test returning retained and not-retained values.
 //===----------------------------------------------------------------------===//




More information about the cfe-commits mailing list