ExprEngineObjC.cpp 12 KB

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