]> granicus.if.org Git - clang/commitdiff
Implemented simple check in <rdar://problem/6600344>: When the receiver of a
authorTed Kremenek <kremenek@apple.com>
Thu, 19 Feb 2009 04:06:22 +0000 (04:06 +0000)
committerTed Kremenek <kremenek@apple.com>
Thu, 19 Feb 2009 04:06:22 +0000 (04:06 +0000)
message expression is nil and the return type is struct then the returned value
is undefined or potentially garbage.

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

include/clang/Analysis/PathSensitive/GRExprEngine.h
lib/Analysis/GRExprEngine.cpp
lib/Analysis/GRExprEngineInternalChecks.cpp

index c2ff5330e4e478a53e5761b8643f6bd415ed5f54..7f2b5f595b8c9e2e52cac49857d6b0ba5d0522e4 100644 (file)
@@ -94,6 +94,16 @@ public:
   typedef llvm::SmallPtrSet<NodeTy*,2> ErrorNodes;  
   typedef llvm::DenseMap<NodeTy*, Expr*> UndefArgsTy;
   
+  /// NilReceiverStructRetExplicit - Nodes in the ExplodedGraph that resulted
+  ///  from [x ...] with 'x' definitely being nil and the result was a 'struct'
+  //  (an undefined value).
+  ErrorNodes NilReceiverStructRetExplicit;
+  
+  /// NilReceiverStructRetImplicit - Nodes in the ExplodedGraph that resulted
+  ///  from [x ...] with 'x' possibly being nil and the result was a 'struct'
+  //  (an undefined value).
+  ErrorNodes NilReceiverStructRetImplicit;
+  
   /// RetsStackAddr - Nodes in the ExplodedGraph that result from returning
   ///  the address of a stack variable.
   ErrorNodes RetsStackAddr;
@@ -300,6 +310,16 @@ public:
     return ImplicitNullDeref.end();
   }
   
+  typedef ErrorNodes::iterator nil_receiver_struct_ret_iterator;
+  
+  nil_receiver_struct_ret_iterator nil_receiver_struct_ret_begin() {
+    return NilReceiverStructRetExplicit.begin();
+  }
+
+  nil_receiver_struct_ret_iterator nil_receiver_struct_ret_end() {
+    return NilReceiverStructRetExplicit.end();
+  }
+  
   typedef ErrorNodes::iterator undef_deref_iterator;
   undef_deref_iterator undef_derefs_begin() { return UndefDeref.begin(); }
   undef_deref_iterator undef_derefs_end() { return UndefDeref.end(); }
index b0554151825b1c3f0c62fdbdf4fd8bcca3ca175b..60bef6eb718662447e543835323b517d8f9cbbd8 100644 (file)
@@ -1514,8 +1514,7 @@ void GRExprEngine::VisitObjCMessageExprDispatchHelper(ObjCMessageExpr* ME,
     
     SVal L = GetSVal(state, Receiver);
     
-    // Check for undefined control-flow or calls to NULL.
-    
+    // Check for undefined control-flow.    
     if (L.isUndef()) {
       NodeTy* N = Builder->generateNode(ME, state, Pred);
       
@@ -1527,6 +1526,33 @@ void GRExprEngine::VisitObjCMessageExprDispatchHelper(ObjCMessageExpr* ME,
       return;
     }
     
+    // "Assume" that the receiver is not NULL.    
+    bool isFeasibleNotNull = false;
+    Assume(state, L, true, isFeasibleNotNull);
+    
+    // "Assume" that the receiver is NULL.    
+    bool isFeasibleNull = false;
+    const GRState *StNull = Assume(state, L, false, isFeasibleNull);
+    
+    if (isFeasibleNull) {
+      // Check if the receiver was nil and the return value a struct.
+      if (ME->getType()->isRecordType()) {
+        // The [0 ...] expressions will return garbage.  Flag either an
+        // explicit or implicit error.  Because of the structure of this
+        // function we currently do not bifurfacte the state graph at
+        // this point.
+        // FIXME: We should bifurcate and fill the returned struct with
+        //  garbage.                
+        if (NodeTy* N = Builder->generateNode(ME, StNull, Pred)) {
+          N->markAsSink();
+          if (isFeasibleNotNull)
+            NilReceiverStructRetImplicit.insert(N);
+          else
+            NilReceiverStructRetExplicit.insert(N);
+        }
+      }
+    }
+    
     // Check if the "raise" message was sent.
     if (ME->getSelector() == RaiseSel)
       RaisesException = true;
index b7762d8412b4961728f58c5edc5409b845464ec0..135f746ba1c2860c24ce4c27a8913306bb95c0ea 100644 (file)
@@ -71,6 +71,34 @@ public:
   }
 };
   
+class VISIBILITY_HIDDEN NilReceiverStructRet : public BugType {
+  GRExprEngine &Eng;
+public:
+  NilReceiverStructRet(GRExprEngine* eng) :
+    BugType("nil receiver with struct return type", "Logic Errors"),  
+    Eng(*eng) {}
+
+  void FlushReports(BugReporter& BR) {
+    for (GRExprEngine::nil_receiver_struct_ret_iterator
+          I=Eng.nil_receiver_struct_ret_begin(),
+          E=Eng.nil_receiver_struct_ret_end(); I!=E; ++I) {
+
+      std::string sbuf;
+      llvm::raw_string_ostream os(sbuf);
+      PostStmt P = cast<PostStmt>((*I)->getLocation());
+      ObjCMessageExpr *ME = cast<ObjCMessageExpr>(P.getStmt());
+      os << "The receiver in the message expression is 'nil' and results in the"
+            " returned value (of type '"
+         << ME->getType().getAsString()
+         << "') to be garbage or otherwise undefined.";
+
+      RangedBugReport *R = new RangedBugReport(*this, os.str().c_str(), *I);
+      R->addRange(ME->getReceiver()->getSourceRange());
+      BR.EmitReport(R);
+    }
+  }
+};
+  
 class VISIBILITY_HIDDEN UndefinedDeref : public BuiltinBug {
 public:
   UndefinedDeref(GRExprEngine* eng)
@@ -142,7 +170,8 @@ public:
     for (GRExprEngine::UndefArgsTy::iterator I=Eng.msg_expr_undef_arg_begin(),
          E = Eng.msg_expr_undef_arg_end(); I!=E; ++I) {      
       // Generate a report for this bug.
-      RangedBugReport *report = new RangedBugReport(*this, desc.c_str(), I->first);
+      RangedBugReport *report = new RangedBugReport(*this, desc.c_str(),
+                                                    I->first);
       report->addRange(I->second->getSourceRange());
       BR.EmitReport(report);
     }    
@@ -431,6 +460,7 @@ void GRExprEngine::RegisterInternalChecks() {
   BR.Register(new BadReceiver(this));
   BR.Register(new OutOfBoundMemoryAccess(this));
   BR.Register(new BadSizeVLA(this));
+  BR.Register(new NilReceiverStructRet(this));
   
   // The following checks do not need to have their associated BugTypes
   // explicitly registered with the BugReporter.  If they issue any BugReports,