xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
10b57cec5SDimitry Andric //=-- ExprEngineObjC.cpp - ExprEngine support for Objective-C ---*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric //  This file defines ExprEngine's support for Objective-C expressions.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric 
130b57cec5SDimitry Andric #include "clang/AST/StmtObjC.h"
140b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
150b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
160b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
170b57cec5SDimitry Andric 
180b57cec5SDimitry Andric using namespace clang;
190b57cec5SDimitry Andric using namespace ento;
200b57cec5SDimitry Andric 
VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr * Ex,ExplodedNode * Pred,ExplodedNodeSet & Dst)210b57cec5SDimitry Andric void ExprEngine::VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr *Ex,
220b57cec5SDimitry Andric                                           ExplodedNode *Pred,
230b57cec5SDimitry Andric                                           ExplodedNodeSet &Dst) {
240b57cec5SDimitry Andric   ProgramStateRef state = Pred->getState();
250b57cec5SDimitry Andric   const LocationContext *LCtx = Pred->getLocationContext();
260b57cec5SDimitry Andric   SVal baseVal = state->getSVal(Ex->getBase(), LCtx);
270b57cec5SDimitry Andric   SVal location = state->getLValue(Ex->getDecl(), baseVal);
280b57cec5SDimitry Andric 
290b57cec5SDimitry Andric   ExplodedNodeSet dstIvar;
300b57cec5SDimitry Andric   StmtNodeBuilder Bldr(Pred, dstIvar, *currBldrCtx);
310b57cec5SDimitry Andric   Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, location));
320b57cec5SDimitry Andric 
330b57cec5SDimitry Andric   // Perform the post-condition check of the ObjCIvarRefExpr and store
340b57cec5SDimitry Andric   // the created nodes in 'Dst'.
350b57cec5SDimitry Andric   getCheckerManager().runCheckersForPostStmt(Dst, dstIvar, Ex, *this);
360b57cec5SDimitry Andric }
370b57cec5SDimitry Andric 
VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt * S,ExplodedNode * Pred,ExplodedNodeSet & Dst)380b57cec5SDimitry Andric void ExprEngine::VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S,
390b57cec5SDimitry Andric                                              ExplodedNode *Pred,
400b57cec5SDimitry Andric                                              ExplodedNodeSet &Dst) {
410b57cec5SDimitry Andric   getCheckerManager().runCheckersForPreStmt(Dst, Pred, S, *this);
420b57cec5SDimitry Andric }
430b57cec5SDimitry Andric 
440b57cec5SDimitry Andric /// Generate a node in \p Bldr for an iteration statement using ObjC
450b57cec5SDimitry Andric /// for-loop iterator.
populateObjCForDestinationSet(ExplodedNodeSet & dstLocation,SValBuilder & svalBuilder,const ObjCForCollectionStmt * S,const Stmt * elem,SVal elementV,SymbolManager & SymMgr,const NodeBuilderContext * currBldrCtx,StmtNodeBuilder & Bldr,bool hasElements)460b57cec5SDimitry Andric static void populateObjCForDestinationSet(
470b57cec5SDimitry Andric     ExplodedNodeSet &dstLocation, SValBuilder &svalBuilder,
480b57cec5SDimitry Andric     const ObjCForCollectionStmt *S, const Stmt *elem, SVal elementV,
490b57cec5SDimitry Andric     SymbolManager &SymMgr, const NodeBuilderContext *currBldrCtx,
500b57cec5SDimitry Andric     StmtNodeBuilder &Bldr, bool hasElements) {
510b57cec5SDimitry Andric 
520b57cec5SDimitry Andric   for (ExplodedNode *Pred : dstLocation) {
530b57cec5SDimitry Andric     ProgramStateRef state = Pred->getState();
540b57cec5SDimitry Andric     const LocationContext *LCtx = Pred->getLocationContext();
550b57cec5SDimitry Andric 
56e8d8bef9SDimitry Andric     ProgramStateRef nextState =
57e8d8bef9SDimitry Andric         ExprEngine::setWhetherHasMoreIteration(state, S, LCtx, hasElements);
580b57cec5SDimitry Andric 
590b57cec5SDimitry Andric     if (auto MV = elementV.getAs<loc::MemRegionVal>())
600b57cec5SDimitry Andric       if (const auto *R = dyn_cast<TypedValueRegion>(MV->getRegion())) {
610b57cec5SDimitry Andric         // FIXME: The proper thing to do is to really iterate over the
620b57cec5SDimitry Andric         //  container.  We will do this with dispatch logic to the store.
630b57cec5SDimitry Andric         //  For now, just 'conjure' up a symbolic value.
640b57cec5SDimitry Andric         QualType T = R->getValueType();
650b57cec5SDimitry Andric         assert(Loc::isLocType(T));
660b57cec5SDimitry Andric 
670b57cec5SDimitry Andric         SVal V;
680b57cec5SDimitry Andric         if (hasElements) {
690b57cec5SDimitry Andric           SymbolRef Sym = SymMgr.conjureSymbol(elem, LCtx, T,
700b57cec5SDimitry Andric                                                currBldrCtx->blockCount());
710b57cec5SDimitry Andric           V = svalBuilder.makeLoc(Sym);
720b57cec5SDimitry Andric         } else {
730b57cec5SDimitry Andric           V = svalBuilder.makeIntVal(0, T);
740b57cec5SDimitry Andric         }
750b57cec5SDimitry Andric 
760b57cec5SDimitry Andric         nextState = nextState->bindLoc(elementV, V, LCtx);
770b57cec5SDimitry Andric       }
780b57cec5SDimitry Andric 
790b57cec5SDimitry Andric     Bldr.generateNode(S, Pred, nextState);
800b57cec5SDimitry Andric   }
810b57cec5SDimitry Andric }
820b57cec5SDimitry Andric 
VisitObjCForCollectionStmt(const ObjCForCollectionStmt * S,ExplodedNode * Pred,ExplodedNodeSet & Dst)830b57cec5SDimitry Andric void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
840b57cec5SDimitry Andric                                             ExplodedNode *Pred,
850b57cec5SDimitry Andric                                             ExplodedNodeSet &Dst) {
860b57cec5SDimitry Andric 
870b57cec5SDimitry Andric   // ObjCForCollectionStmts are processed in two places.  This method
880b57cec5SDimitry Andric   // handles the case where an ObjCForCollectionStmt* occurs as one of the
890b57cec5SDimitry Andric   // statements within a basic block.  This transfer function does two things:
900b57cec5SDimitry Andric   //
910b57cec5SDimitry Andric   //  (1) binds the next container value to 'element'.  This creates a new
920b57cec5SDimitry Andric   //      node in the ExplodedGraph.
930b57cec5SDimitry Andric   //
94e8d8bef9SDimitry Andric   //  (2) note whether the collection has any more elements (or in other words,
95e8d8bef9SDimitry Andric   //      whether the loop has more iterations). This will be tested in
96e8d8bef9SDimitry Andric   //      processBranch.
970b57cec5SDimitry Andric   //
980b57cec5SDimitry Andric   // FIXME: Eventually this logic should actually do dispatches to
990b57cec5SDimitry Andric   //   'countByEnumeratingWithState:objects:count:' (NSFastEnumeration).
1000b57cec5SDimitry Andric   //   This will require simulating a temporary NSFastEnumerationState, either
1010b57cec5SDimitry Andric   //   through an SVal or through the use of MemRegions.  This value can
1020b57cec5SDimitry Andric   //   be affixed to the ObjCForCollectionStmt* instead of 0/1; when the loop
1030b57cec5SDimitry Andric   //   terminates we reclaim the temporary (it goes out of scope) and we
1040b57cec5SDimitry Andric   //   we can test if the SVal is 0 or if the MemRegion is null (depending
1050b57cec5SDimitry Andric   //   on what approach we take).
1060b57cec5SDimitry Andric   //
1070b57cec5SDimitry Andric   //  For now: simulate (1) by assigning either a symbol or nil if the
1080b57cec5SDimitry Andric   //    container is empty.  Thus this transfer function will by default
1090b57cec5SDimitry Andric   //    result in state splitting.
1100b57cec5SDimitry Andric 
1110b57cec5SDimitry Andric   const Stmt *elem = S->getElement();
1120b57cec5SDimitry Andric   const Stmt *collection = S->getCollection();
1130b57cec5SDimitry Andric   ProgramStateRef state = Pred->getState();
1140b57cec5SDimitry Andric   SVal collectionV = state->getSVal(collection, Pred->getLocationContext());
1150b57cec5SDimitry Andric 
1160b57cec5SDimitry Andric   SVal elementV;
1170b57cec5SDimitry Andric   if (const auto *DS = dyn_cast<DeclStmt>(elem)) {
1180b57cec5SDimitry Andric     const VarDecl *elemD = cast<VarDecl>(DS->getSingleDecl());
1190b57cec5SDimitry Andric     assert(elemD->getInit() == nullptr);
1200b57cec5SDimitry Andric     elementV = state->getLValue(elemD, Pred->getLocationContext());
1210b57cec5SDimitry Andric   } else {
1220b57cec5SDimitry Andric     elementV = state->getSVal(elem, Pred->getLocationContext());
1230b57cec5SDimitry Andric   }
1240b57cec5SDimitry Andric 
1250b57cec5SDimitry Andric   bool isContainerNull = state->isNull(collectionV).isConstrainedTrue();
1260b57cec5SDimitry Andric 
1270b57cec5SDimitry Andric   ExplodedNodeSet dstLocation;
1280b57cec5SDimitry Andric   evalLocation(dstLocation, S, elem, Pred, state, elementV, false);
1290b57cec5SDimitry Andric 
1300b57cec5SDimitry Andric   ExplodedNodeSet Tmp;
1310b57cec5SDimitry Andric   StmtNodeBuilder Bldr(Pred, Tmp, *currBldrCtx);
1320b57cec5SDimitry Andric 
1330b57cec5SDimitry Andric   if (!isContainerNull)
1340b57cec5SDimitry Andric     populateObjCForDestinationSet(dstLocation, svalBuilder, S, elem, elementV,
1350b57cec5SDimitry Andric                                   SymMgr, currBldrCtx, Bldr,
1360b57cec5SDimitry Andric                                   /*hasElements=*/true);
1370b57cec5SDimitry Andric 
1380b57cec5SDimitry Andric   populateObjCForDestinationSet(dstLocation, svalBuilder, S, elem, elementV,
1390b57cec5SDimitry Andric                                 SymMgr, currBldrCtx, Bldr,
1400b57cec5SDimitry Andric                                 /*hasElements=*/false);
1410b57cec5SDimitry Andric 
1420b57cec5SDimitry Andric   // Finally, run any custom checkers.
1430b57cec5SDimitry Andric   // FIXME: Eventually all pre- and post-checks should live in VisitStmt.
1440b57cec5SDimitry Andric   getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this);
1450b57cec5SDimitry Andric }
1460b57cec5SDimitry Andric 
VisitObjCMessage(const ObjCMessageExpr * ME,ExplodedNode * Pred,ExplodedNodeSet & Dst)1470b57cec5SDimitry Andric void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME,
1480b57cec5SDimitry Andric                                   ExplodedNode *Pred,
1490b57cec5SDimitry Andric                                   ExplodedNodeSet &Dst) {
1500b57cec5SDimitry Andric   CallEventManager &CEMgr = getStateManager().getCallEventManager();
15106c3fb27SDimitry Andric   CallEventRef<ObjCMethodCall> Msg = CEMgr.getObjCMethodCall(
15206c3fb27SDimitry Andric       ME, Pred->getState(), Pred->getLocationContext(), getCFGElementRef());
1530b57cec5SDimitry Andric 
1540b57cec5SDimitry Andric   // There are three cases for the receiver:
1550b57cec5SDimitry Andric   //   (1) it is definitely nil,
1560b57cec5SDimitry Andric   //   (2) it is definitely non-nil, and
1570b57cec5SDimitry Andric   //   (3) we don't know.
1580b57cec5SDimitry Andric   //
1590b57cec5SDimitry Andric   // If the receiver is definitely nil, we skip the pre/post callbacks and
1600b57cec5SDimitry Andric   // instead call the ObjCMessageNil callbacks and return.
1610b57cec5SDimitry Andric   //
1620b57cec5SDimitry Andric   // If the receiver is definitely non-nil, we call the pre- callbacks,
1630b57cec5SDimitry Andric   // evaluate the call, and call the post- callbacks.
1640b57cec5SDimitry Andric   //
1650b57cec5SDimitry Andric   // If we don't know, we drop the potential nil flow and instead
1660b57cec5SDimitry Andric   // continue from the assumed non-nil state as in (2). This approach
1670b57cec5SDimitry Andric   // intentionally drops coverage in order to prevent false alarms
1680b57cec5SDimitry Andric   // in the following scenario:
1690b57cec5SDimitry Andric   //
1700b57cec5SDimitry Andric   //   id result = [o someMethod]
1710b57cec5SDimitry Andric   //   if (result) {
1720b57cec5SDimitry Andric   //     if (!o) {
1730b57cec5SDimitry Andric   //       // <-- This program point should be unreachable because if o is nil
1740b57cec5SDimitry Andric   //       // it must the case that result is nil as well.
1750b57cec5SDimitry Andric   //     }
1760b57cec5SDimitry Andric   //   }
1770b57cec5SDimitry Andric   //
178*5f757f3fSDimitry Andric   // However, it also loses coverage of the nil path prematurely,
179*5f757f3fSDimitry Andric   // leading to missed reports.
180*5f757f3fSDimitry Andric   //
181*5f757f3fSDimitry Andric   // It's possible to handle this by performing a state split on every call:
182*5f757f3fSDimitry Andric   // explore the state where the receiver is non-nil, and independently
183*5f757f3fSDimitry Andric   // explore the state where it's nil. But this is not only slow, but
184*5f757f3fSDimitry Andric   // completely unwarranted. The mere presence of the message syntax in the code
185*5f757f3fSDimitry Andric   // isn't sufficient evidence that nil is a realistic possibility.
186*5f757f3fSDimitry Andric   //
187*5f757f3fSDimitry Andric   // An ideal solution would be to add the following constraint that captures
188*5f757f3fSDimitry Andric   // both possibilities without splitting the state:
189*5f757f3fSDimitry Andric   //
190*5f757f3fSDimitry Andric   //   ($x == 0) => ($y == 0)                                                (1)
191*5f757f3fSDimitry Andric   //
192*5f757f3fSDimitry Andric   // where in our case '$x' is the receiver symbol, '$y' is the returned symbol,
193*5f757f3fSDimitry Andric   // and '=>' is logical implication. But RangeConstraintManager can't handle
194*5f757f3fSDimitry Andric   // such constraints yet, so for now we go with a simpler, more restrictive
195*5f757f3fSDimitry Andric   // constraint: $x != 0, from which (1) follows as a vacuous truth.
1960b57cec5SDimitry Andric   if (Msg->isInstanceMessage()) {
1970b57cec5SDimitry Andric     SVal recVal = Msg->getReceiverSVal();
1980b57cec5SDimitry Andric     if (!recVal.isUndef()) {
1990b57cec5SDimitry Andric       // Bifurcate the state into nil and non-nil ones.
2000b57cec5SDimitry Andric       DefinedOrUnknownSVal receiverVal =
2010b57cec5SDimitry Andric           recVal.castAs<DefinedOrUnknownSVal>();
2020b57cec5SDimitry Andric       ProgramStateRef State = Pred->getState();
2030b57cec5SDimitry Andric 
2040b57cec5SDimitry Andric       ProgramStateRef notNilState, nilState;
2050b57cec5SDimitry Andric       std::tie(notNilState, nilState) = State->assume(receiverVal);
2060b57cec5SDimitry Andric 
2070b57cec5SDimitry Andric       // Receiver is definitely nil, so run ObjCMessageNil callbacks and return.
2080b57cec5SDimitry Andric       if (nilState && !notNilState) {
2090b57cec5SDimitry Andric         ExplodedNodeSet dstNil;
2100b57cec5SDimitry Andric         StmtNodeBuilder Bldr(Pred, dstNil, *currBldrCtx);
2110b57cec5SDimitry Andric         bool HasTag = Pred->getLocation().getTag();
2120b57cec5SDimitry Andric         Pred = Bldr.generateNode(ME, Pred, nilState, nullptr,
2130b57cec5SDimitry Andric                                  ProgramPoint::PreStmtKind);
2140b57cec5SDimitry Andric         assert((Pred || HasTag) && "Should have cached out already!");
2150b57cec5SDimitry Andric         (void)HasTag;
2160b57cec5SDimitry Andric         if (!Pred)
2170b57cec5SDimitry Andric           return;
2180b57cec5SDimitry Andric 
2190b57cec5SDimitry Andric         ExplodedNodeSet dstPostCheckers;
2200b57cec5SDimitry Andric         getCheckerManager().runCheckersForObjCMessageNil(dstPostCheckers, Pred,
2210b57cec5SDimitry Andric                                                          *Msg, *this);
222bdd1243dSDimitry Andric         for (auto *I : dstPostCheckers)
2230b57cec5SDimitry Andric           finishArgumentConstruction(Dst, I, *Msg);
2240b57cec5SDimitry Andric         return;
2250b57cec5SDimitry Andric       }
2260b57cec5SDimitry Andric 
2270b57cec5SDimitry Andric       ExplodedNodeSet dstNonNil;
2280b57cec5SDimitry Andric       StmtNodeBuilder Bldr(Pred, dstNonNil, *currBldrCtx);
2290b57cec5SDimitry Andric       // Generate a transition to the non-nil state, dropping any potential
2300b57cec5SDimitry Andric       // nil flow.
2310b57cec5SDimitry Andric       if (notNilState != State) {
2320b57cec5SDimitry Andric         bool HasTag = Pred->getLocation().getTag();
2330b57cec5SDimitry Andric         Pred = Bldr.generateNode(ME, Pred, notNilState);
2340b57cec5SDimitry Andric         assert((Pred || HasTag) && "Should have cached out already!");
2350b57cec5SDimitry Andric         (void)HasTag;
2360b57cec5SDimitry Andric         if (!Pred)
2370b57cec5SDimitry Andric           return;
2380b57cec5SDimitry Andric       }
2390b57cec5SDimitry Andric     }
2400b57cec5SDimitry Andric   }
2410b57cec5SDimitry Andric 
2420b57cec5SDimitry Andric   // Handle the previsits checks.
2430b57cec5SDimitry Andric   ExplodedNodeSet dstPrevisit;
2440b57cec5SDimitry Andric   getCheckerManager().runCheckersForPreObjCMessage(dstPrevisit, Pred,
2450b57cec5SDimitry Andric                                                    *Msg, *this);
2460b57cec5SDimitry Andric   ExplodedNodeSet dstGenericPrevisit;
2470b57cec5SDimitry Andric   getCheckerManager().runCheckersForPreCall(dstGenericPrevisit, dstPrevisit,
2480b57cec5SDimitry Andric                                             *Msg, *this);
2490b57cec5SDimitry Andric 
2500b57cec5SDimitry Andric   // Proceed with evaluate the message expression.
2510b57cec5SDimitry Andric   ExplodedNodeSet dstEval;
2520b57cec5SDimitry Andric   StmtNodeBuilder Bldr(dstGenericPrevisit, dstEval, *currBldrCtx);
2530b57cec5SDimitry Andric 
2540b57cec5SDimitry Andric   for (ExplodedNodeSet::iterator DI = dstGenericPrevisit.begin(),
2550b57cec5SDimitry Andric        DE = dstGenericPrevisit.end(); DI != DE; ++DI) {
2560b57cec5SDimitry Andric     ExplodedNode *Pred = *DI;
2570b57cec5SDimitry Andric     ProgramStateRef State = Pred->getState();
2580b57cec5SDimitry Andric     CallEventRef<ObjCMethodCall> UpdatedMsg = Msg.cloneWithState(State);
2590b57cec5SDimitry Andric 
2600b57cec5SDimitry Andric     if (UpdatedMsg->isInstanceMessage()) {
2610b57cec5SDimitry Andric       SVal recVal = UpdatedMsg->getReceiverSVal();
2620b57cec5SDimitry Andric       if (!recVal.isUndef()) {
2630b57cec5SDimitry Andric         if (ObjCNoRet.isImplicitNoReturn(ME)) {
2640b57cec5SDimitry Andric           // If we raise an exception, for now treat it as a sink.
2650b57cec5SDimitry Andric           // Eventually we will want to handle exceptions properly.
2660b57cec5SDimitry Andric           Bldr.generateSink(ME, Pred, State);
2670b57cec5SDimitry Andric           continue;
2680b57cec5SDimitry Andric         }
2690b57cec5SDimitry Andric       }
2700b57cec5SDimitry Andric     } else {
2710b57cec5SDimitry Andric       // Check for special class methods that are known to not return
2720b57cec5SDimitry Andric       // and that we should treat as a sink.
2730b57cec5SDimitry Andric       if (ObjCNoRet.isImplicitNoReturn(ME)) {
2740b57cec5SDimitry Andric         // If we raise an exception, for now treat it as a sink.
2750b57cec5SDimitry Andric         // Eventually we will want to handle exceptions properly.
2760b57cec5SDimitry Andric         Bldr.generateSink(ME, Pred, Pred->getState());
2770b57cec5SDimitry Andric         continue;
2780b57cec5SDimitry Andric       }
2790b57cec5SDimitry Andric     }
2800b57cec5SDimitry Andric 
2810b57cec5SDimitry Andric     defaultEvalCall(Bldr, Pred, *UpdatedMsg);
2820b57cec5SDimitry Andric   }
2830b57cec5SDimitry Andric 
2840b57cec5SDimitry Andric   // If there were constructors called for object-type arguments, clean them up.
2850b57cec5SDimitry Andric   ExplodedNodeSet dstArgCleanup;
286bdd1243dSDimitry Andric   for (auto *I : dstEval)
2870b57cec5SDimitry Andric     finishArgumentConstruction(dstArgCleanup, I, *Msg);
2880b57cec5SDimitry Andric 
2890b57cec5SDimitry Andric   ExplodedNodeSet dstPostvisit;
2900b57cec5SDimitry Andric   getCheckerManager().runCheckersForPostCall(dstPostvisit, dstArgCleanup,
2910b57cec5SDimitry Andric                                              *Msg, *this);
2920b57cec5SDimitry Andric 
2930b57cec5SDimitry Andric   // Finally, perform the post-condition check of the ObjCMessageExpr and store
2940b57cec5SDimitry Andric   // the created nodes in 'Dst'.
2950b57cec5SDimitry Andric   getCheckerManager().runCheckersForPostObjCMessage(Dst, dstPostvisit,
2960b57cec5SDimitry Andric                                                     *Msg, *this);
2970b57cec5SDimitry Andric }
298