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 18 using namespace clang; 19 using namespace ento; 20 21 void 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(Pred, dstIvar, *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(Dst, dstIvar, Ex, *this); 36 } 37 38 void ExprEngine::VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S, 39 ExplodedNode *Pred, 40 ExplodedNodeSet &Dst) { 41 getCheckerManager().runCheckersForPreStmt(Dst, Pred, S, *this); 42 } 43 44 /// Generate a node in \p Bldr for an iteration statement using ObjC 45 /// for-loop iterator. 46 static void populateObjCForDestinationSet( 47 ExplodedNodeSet &dstLocation, SValBuilder &svalBuilder, 48 const ObjCForCollectionStmt *S, const Stmt *elem, SVal elementV, 49 SymbolManager &SymMgr, const NodeBuilderContext *currBldrCtx, 50 StmtNodeBuilder &Bldr, bool 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 85 void 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 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(Pred, Tmp, *currBldrCtx); 135 136 if (!isContainerNull) 137 populateObjCForDestinationSet(dstLocation, svalBuilder, S, elem, elementV, 138 SymMgr, currBldrCtx, Bldr, 139 /*hasElements=*/true); 140 141 populateObjCForDestinationSet(dstLocation, svalBuilder, S, elem, elementV, 142 SymMgr, currBldrCtx, Bldr, 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(Dst, Tmp, S, *this); 148 } 149 150 void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, 151 ExplodedNode *Pred, 152 ExplodedNodeSet &Dst) { 153 CallEventManager &CEMgr = getStateManager().getCallEventManager(); 154 CallEventRef<ObjCMethodCall> Msg = 155 CEMgr.getObjCMethodCall(ME, Pred->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 notNilState, nilState; 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(Pred, dstNil, *currBldrCtx); 201 bool HasTag = Pred->getLocation().getTag(); 202 Pred = Bldr.generateNode(ME, Pred, nilState, nullptr, 203 ProgramPoint::PreStmtKind); 204 assert((Pred || HasTag) && "Should have cached out already!"); 205 (void)HasTag; 206 if (!Pred) 207 return; 208 209 ExplodedNodeSet dstPostCheckers; 210 getCheckerManager().runCheckersForObjCMessageNil(dstPostCheckers, Pred, 211 *Msg, *this); 212 for (auto I : dstPostCheckers) 213 finishArgumentConstruction(Dst, I, *Msg); 214 return; 215 } 216 217 ExplodedNodeSet dstNonNil; 218 StmtNodeBuilder Bldr(Pred, dstNonNil, *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 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(dstPrevisit, Pred, 235 *Msg, *this); 236 ExplodedNodeSet dstGenericPrevisit; 237 getCheckerManager().runCheckersForPreCall(dstGenericPrevisit, dstPrevisit, 238 *Msg, *this); 239 240 // Proceed with evaluate the message expression. 241 ExplodedNodeSet dstEval; 242 StmtNodeBuilder Bldr(dstGenericPrevisit, dstEval, *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(Dst, dstPostvisit, 286 *Msg, *this); 287 } 288