Clang Project

clang_source_code/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
1//=-- ExprEngineObjC.cpp - ExprEngine support for Objective-C ---*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9//  This file defines ExprEngine's support for Objective-C expressions.
10//
11//===----------------------------------------------------------------------===//
12
13#include "clang/AST/StmtObjC.h"
14#include "clang/StaticAnalyzer/Core/CheckerManager.h"
15#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
16#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
17
18using namespace clang;
19using namespace ento;
20
21void ExprEngine::VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr *Ex,
22                                          ExplodedNode *Pred,
23                                          ExplodedNodeSet &Dst) {
24  ProgramStateRef state = Pred->getState();
25  const LocationContext *LCtx = Pred->getLocationContext();
26  SVal baseVal = state->getSVal(Ex->getBase(), LCtx);
27  SVal location = state->getLValue(Ex->getDecl(), baseVal);
28
29  ExplodedNodeSet dstIvar;
30  StmtNodeBuilder Bldr(PreddstIvar, *currBldrCtx);
31  Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, location));
32
33  // Perform the post-condition check of the ObjCIvarRefExpr and store
34  // the created nodes in 'Dst'.
35  getCheckerManager().runCheckersForPostStmt(DstdstIvarEx*this);
36}
37
38void ExprEngine::VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S,
39                                             ExplodedNode *Pred,
40                                             ExplodedNodeSet &Dst) {
41  getCheckerManager().runCheckersForPreStmt(DstPredS*this);
42}
43
44/// Generate a node in \p Bldr for an iteration statement using ObjC
45/// for-loop iterator.
46static void populateObjCForDestinationSet(
47    ExplodedNodeSet &dstLocationSValBuilder &svalBuilder,
48    const ObjCForCollectionStmt *Sconst Stmt *elemSVal elementV,
49    SymbolManager &SymMgrconst NodeBuilderContext *currBldrCtx,
50    StmtNodeBuilder &Bldrbool hasElements) {
51
52  for (ExplodedNode *Pred : dstLocation) {
53    ProgramStateRef state = Pred->getState();
54    const LocationContext *LCtx = Pred->getLocationContext();
55
56    SVal hasElementsV = svalBuilder.makeTruthVal(hasElements);
57
58    // FIXME: S is not an expression. We should not be binding values to it.
59    ProgramStateRef nextState = state->BindExpr(S, LCtx, hasElementsV);
60
61    if (auto MV = elementV.getAs<loc::MemRegionVal>())
62      if (const auto *R = dyn_cast<TypedValueRegion>(MV->getRegion())) {
63        // FIXME: The proper thing to do is to really iterate over the
64        //  container.  We will do this with dispatch logic to the store.
65        //  For now, just 'conjure' up a symbolic value.
66        QualType T = R->getValueType();
67        assert(Loc::isLocType(T));
68
69        SVal V;
70        if (hasElements) {
71          SymbolRef Sym = SymMgr.conjureSymbol(elem, LCtx, T,
72                                               currBldrCtx->blockCount());
73          V = svalBuilder.makeLoc(Sym);
74        } else {
75          V = svalBuilder.makeIntVal(0, T);
76        }
77
78        nextState = nextState->bindLoc(elementV, V, LCtx);
79      }
80
81    Bldr.generateNode(S, Pred, nextState);
82  }
83}
84
85void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
86                                            ExplodedNode *Pred,
87                                            ExplodedNodeSet &Dst) {
88
89  // ObjCForCollectionStmts are processed in two places.  This method
90  // handles the case where an ObjCForCollectionStmt* occurs as one of the
91  // statements within a basic block.  This transfer function does two things:
92  //
93  //  (1) binds the next container value to 'element'.  This creates a new
94  //      node in the ExplodedGraph.
95  //
96  //  (2) binds the value 0/1 to the ObjCForCollectionStmt* itself, indicating
97  //      whether or not the container has any more elements.  This value
98  //      will be tested in ProcessBranch.  We need to explicitly bind
99  //      this value because a container can contain nil elements.
100  //
101  // FIXME: Eventually this logic should actually do dispatches to
102  //   'countByEnumeratingWithState:objects:count:' (NSFastEnumeration).
103  //   This will require simulating a temporary NSFastEnumerationState, either
104  //   through an SVal or through the use of MemRegions.  This value can
105  //   be affixed to the ObjCForCollectionStmt* instead of 0/1; when the loop
106  //   terminates we reclaim the temporary (it goes out of scope) and we
107  //   we can test if the SVal is 0 or if the MemRegion is null (depending
108  //   on what approach we take).
109  //
110  //  For now: simulate (1) by assigning either a symbol or nil if the
111  //    container is empty.  Thus this transfer function will by default
112  //    result in state splitting.
113
114  const Stmt *elem = S->getElement();
115  const Stmt *collection = S->getCollection();
116  ProgramStateRef state = Pred->getState();
117  SVal collectionV = state->getSVal(collection, Pred->getLocationContext());
118
119  SVal elementV;
120  if (const auto *DS = dyn_cast<DeclStmt>(elem)) {
121    const VarDecl *elemD = cast<VarDecl>(DS->getSingleDecl());
122    getInit() == nullptr", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp", 122, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(elemD->getInit() == nullptr);
123    elementV = state->getLValue(elemD, Pred->getLocationContext());
124  } else {
125    elementV = state->getSVal(elem, Pred->getLocationContext());
126  }
127
128  bool isContainerNull = state->isNull(collectionV).isConstrainedTrue();
129
130  ExplodedNodeSet dstLocation;
131  evalLocation(dstLocation, S, elem, Pred, state, elementV, false);
132
133  ExplodedNodeSet Tmp;
134  StmtNodeBuilder Bldr(PredTmp, *currBldrCtx);
135
136  if (!isContainerNull)
137    populateObjCForDestinationSet(dstLocationsvalBuilderSelemelementV,
138                                  SymMgrcurrBldrCtxBldr,
139                                  /*hasElements=*/true);
140
141  populateObjCForDestinationSet(dstLocationsvalBuilderSelemelementV,
142                                SymMgrcurrBldrCtxBldr,
143                                /*hasElements=*/false);
144
145  // Finally, run any custom checkers.
146  // FIXME: Eventually all pre- and post-checks should live in VisitStmt.
147  getCheckerManager().runCheckersForPostStmt(DstTmpS*this);
148}
149
150void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME,
151                                  ExplodedNode *Pred,
152                                  ExplodedNodeSet &Dst) {
153  CallEventManager &CEMgr = getStateManager().getCallEventManager();
154  CallEventRef<ObjCMethodCallMsg =
155    CEMgr.getObjCMethodCall(MEPred->getState(), Pred->getLocationContext());
156
157  // There are three cases for the receiver:
158  //   (1) it is definitely nil,
159  //   (2) it is definitely non-nil, and
160  //   (3) we don't know.
161  //
162  // If the receiver is definitely nil, we skip the pre/post callbacks and
163  // instead call the ObjCMessageNil callbacks and return.
164  //
165  // If the receiver is definitely non-nil, we call the pre- callbacks,
166  // evaluate the call, and call the post- callbacks.
167  //
168  // If we don't know, we drop the potential nil flow and instead
169  // continue from the assumed non-nil state as in (2). This approach
170  // intentionally drops coverage in order to prevent false alarms
171  // in the following scenario:
172  //
173  // id result = [o someMethod]
174  // if (result) {
175  //   if (!o) {
176  //     // <-- This program point should be unreachable because if o is nil
177  //     // it must the case that result is nil as well.
178  //   }
179  // }
180  //
181  // We could avoid dropping coverage by performing an explicit case split
182  // on each method call -- but this would get very expensive. An alternative
183  // would be to introduce lazy constraints.
184  // FIXME: This ignores many potential bugs (<rdar://problem/11733396>).
185  // Revisit once we have lazier constraints.
186  if (Msg->isInstanceMessage()) {
187    SVal recVal = Msg->getReceiverSVal();
188    if (!recVal.isUndef()) {
189      // Bifurcate the state into nil and non-nil ones.
190      DefinedOrUnknownSVal receiverVal =
191          recVal.castAs<DefinedOrUnknownSVal>();
192      ProgramStateRef State = Pred->getState();
193
194      ProgramStateRef notNilStatenilState;
195      std::tie(notNilState, nilState) = State->assume(receiverVal);
196
197      // Receiver is definitely nil, so run ObjCMessageNil callbacks and return.
198      if (nilState && !notNilState) {
199        ExplodedNodeSet dstNil;
200        StmtNodeBuilder Bldr(PreddstNil, *currBldrCtx);
201        bool HasTag = Pred->getLocation().getTag();
202        Pred = Bldr.generateNode(ME, Pred, nilState, nullptr,
203                                 ProgramPoint::PreStmtKind);
204         (0) . __assert_fail ("(Pred || HasTag) && \"Should have cached out already!\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp", 204, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert((Pred || HasTag) && "Should have cached out already!");
205        (void)HasTag;
206        if (!Pred)
207          return;
208
209        ExplodedNodeSet dstPostCheckers;
210        getCheckerManager().runCheckersForObjCMessageNil(dstPostCheckersPred,
211                                                         *Msg*this);
212        for (auto I : dstPostCheckers)
213          finishArgumentConstruction(Dst, I, *Msg);
214        return;
215      }
216
217      ExplodedNodeSet dstNonNil;
218      StmtNodeBuilder Bldr(PreddstNonNil, *currBldrCtx);
219      // Generate a transition to the non-nil state, dropping any potential
220      // nil flow.
221      if (notNilState != State) {
222        bool HasTag = Pred->getLocation().getTag();
223        Pred = Bldr.generateNode(ME, Pred, notNilState);
224         (0) . __assert_fail ("(Pred || HasTag) && \"Should have cached out already!\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp", 224, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert((Pred || HasTag) && "Should have cached out already!");
225        (void)HasTag;
226        if (!Pred)
227          return;
228      }
229    }
230  }
231
232  // Handle the previsits checks.
233  ExplodedNodeSet dstPrevisit;
234  getCheckerManager().runCheckersForPreObjCMessage(dstPrevisitPred,
235                                                   *Msg*this);
236  ExplodedNodeSet dstGenericPrevisit;
237  getCheckerManager().runCheckersForPreCall(dstGenericPrevisitdstPrevisit,
238                                            *Msg*this);
239
240  // Proceed with evaluate the message expression.
241  ExplodedNodeSet dstEval;
242  StmtNodeBuilder Bldr(dstGenericPrevisitdstEval, *currBldrCtx);
243
244  for (ExplodedNodeSet::iterator DI = dstGenericPrevisit.begin(),
245       DE = dstGenericPrevisit.end(); DI != DE; ++DI) {
246    ExplodedNode *Pred = *DI;
247    ProgramStateRef State = Pred->getState();
248    CallEventRef<ObjCMethodCall> UpdatedMsg = Msg.cloneWithState(State);
249
250    if (UpdatedMsg->isInstanceMessage()) {
251      SVal recVal = UpdatedMsg->getReceiverSVal();
252      if (!recVal.isUndef()) {
253        if (ObjCNoRet.isImplicitNoReturn(ME)) {
254          // If we raise an exception, for now treat it as a sink.
255          // Eventually we will want to handle exceptions properly.
256          Bldr.generateSink(ME, Pred, State);
257          continue;
258        }
259      }
260    } else {
261      // Check for special class methods that are known to not return
262      // and that we should treat as a sink.
263      if (ObjCNoRet.isImplicitNoReturn(ME)) {
264        // If we raise an exception, for now treat it as a sink.
265        // Eventually we will want to handle exceptions properly.
266        Bldr.generateSink(ME, Pred, Pred->getState());
267        continue;
268      }
269    }
270
271    defaultEvalCall(Bldr, Pred, *UpdatedMsg);
272  }
273
274  // If there were constructors called for object-type arguments, clean them up.
275  ExplodedNodeSet dstArgCleanup;
276  for (auto I : dstEval)
277    finishArgumentConstruction(dstArgCleanup, I, *Msg);
278
279  ExplodedNodeSet dstPostvisit;
280  getCheckerManager().runCheckersForPostCall(dstPostvisit, dstArgCleanup,
281                                             *Msg, *this);
282
283  // Finally, perform the post-condition check of the ObjCMessageExpr and store
284  // the created nodes in 'Dst'.
285  getCheckerManager().runCheckersForPostObjCMessage(DstdstPostvisit,
286                                                    *Msg*this);
287}
288
clang::ento::ExprEngine::VisitLvalObjCIvarRefExpr
clang::ento::ExprEngine::VisitObjCAtSynchronizedStmt
clang::ento::ExprEngine::VisitObjCForCollectionStmt
clang::ento::ExprEngine::VisitObjCMessage