// At the post visit stage, the predecessor ExplodedNode will be the // BinaryOperator that was just created. We use this hook to collect the // ExplodedNode. void IdempotentOperationChecker::PostVisitBinaryOperator( CheckerContext &C, const BinaryOperator *B) { // Add the ExplodedNode we just visited BinaryOperatorData &Data = hash[B]; assert(isa<BinaryOperator>(cast<StmtPoint>(C.getPredecessor() ->getLocation()).getStmt())); Data.explodedNodes.Add(C.getPredecessor()); }
void NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMethodCall &Msg, CheckerContext &C) const { // Check if the method is annotated with analyzer_noreturn. if (const ObjCMethodDecl *MD = Msg.getDecl()) { MD = MD->getCanonicalDecl(); if (MD->hasAttr<AnalyzerNoReturnAttr>()) { C.generateSink(C.getState(), C.getPredecessor()); return; } } // HACK: This entire check is to handle two messages in the Cocoa frameworks: // -[NSAssertionHandler // handleFailureInMethod:object:file:lineNumber:description:] // -[NSAssertionHandler // handleFailureInFunction:file:lineNumber:description:] // Eventually these should be annotated with __attribute__((noreturn)). // Because ObjC messages use dynamic dispatch, it is not generally safe to // assume certain methods can't return. In cases where it is definitely valid, // see if you can mark the methods noreturn or analyzer_noreturn instead of // adding more explicit checks to this method. if (!Msg.isInstanceMessage()) return; const ObjCInterfaceDecl *Receiver = Msg.getReceiverInterface(); if (!Receiver) return; if (!Receiver->getIdentifier()->isStr("NSAssertionHandler")) return; Selector Sel = Msg.getSelector(); switch (Sel.getNumArgs()) { default: return; case 4: lazyInitKeywordSelector(HandleFailureInFunctionSel, C.getASTContext(), "handleFailureInFunction", "file", "lineNumber", "description"); if (Sel != HandleFailureInFunctionSel) return; break; case 5: lazyInitKeywordSelector(HandleFailureInMethodSel, C.getASTContext(), "handleFailureInMethod", "object", "file", "lineNumber", "description"); if (Sel != HandleFailureInMethodSel) return; break; } // If we got here, it's one of the messages we care about. C.generateSink(C.getState(), C.getPredecessor()); }
// At the post visit stage, the predecessor ExplodedNode will be the // BinaryOperator that was just created. We use this hook to collect the // ExplodedNode. void IdempotentOperationChecker::checkPostStmt(const BinaryOperator *B, CheckerContext &C) const { // Add the ExplodedNode we just visited BinaryOperatorData &Data = hash[B]; const Stmt *predStmt = cast<StmtPoint>(C.getPredecessor()->getLocation()).getStmt(); // Ignore implicit calls to setters. if (!isa<BinaryOperator>(predStmt)) return; Data.explodedNodes.Add(C.getPredecessor()); }
void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, SVal lock, bool isTryLock) { const MemRegion *lockR = lock.getAsRegion(); if (!lockR) return; const GRState *state = C.getState(); SVal X = state->getSVal(CE); if (X.isUnknownOrUndef()) return; DefinedSVal retVal = cast<DefinedSVal>(X); const GRState *lockSucc = state; if (isTryLock) { // Bifurcate the state, and allow a mode where the lock acquisition fails. const GRState *lockFail; llvm::tie(lockFail, lockSucc) = state->assume(retVal); assert(lockFail && lockSucc); C.addTransition(C.generateNode(CE, lockFail)); } else { // Assume that the return value was 0. lockSucc = state->assume(retVal, false); assert(lockSucc); } // Record that the lock was acquired. lockSucc = lockSucc->add<LockSet>(lockR); C.addTransition(lockSucc != state ? C.generateNode(CE, lockSucc) : C.getPredecessor()); }
void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const { ProgramStateRef state = C.getState(); SValBuilder &svalBuilder = C.getSValBuilder(); const LocationContext *LCtx = C.getPredecessor()->getLocationContext(); DefinedSVal RetVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()) .castAs<DefinedSVal>(); state = state->BindExpr(CE, C.getLocationContext(), RetVal); ConstraintManager &CM = C.getConstraintManager(); // Bifurcate the state into two: one with a valid FILE* pointer, the other // with a NULL. ProgramStateRef stateNotNull, stateNull; std::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal); if (SymbolRef Sym = RetVal.getAsSymbol()) { // if RetVal is not NULL, set the symbol's state to Opened. stateNotNull = stateNotNull->set<StreamMap>(Sym,StreamState::getOpened(CE)); stateNull = stateNull->set<StreamMap>(Sym, StreamState::getOpenFailed(CE)); C.addTransition(stateNotNull); C.addTransition(stateNull); } }
void IteratorChecker::handleComparison(CheckerContext &C, const SVal &RetVal, const SVal &LVal, const SVal &RVal, OverloadedOperatorKind Op) const { // Record the operands and the operator of the comparison for the next // evalAssume, if the result is a symbolic expression. If it is a concrete // value (only one branch is possible), then transfer the state between // the operands according to the operator and the result auto State = C.getState(); if (const auto *Condition = RetVal.getAsSymbolicExpression()) { const auto *LPos = getIteratorPosition(State, LVal); const auto *RPos = getIteratorPosition(State, RVal); if (!LPos && !RPos) return; State = saveComparison(State, Condition, LVal, RVal, Op == OO_EqualEqual); C.addTransition(State); } else if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) { if ((State = processComparison( State, getRegionOrSymbol(LVal), getRegionOrSymbol(RVal), (Op == OO_EqualEqual) == (TruthVal->getValue() != 0)))) { C.addTransition(State); } else { C.generateSink(State, C.getPredecessor()); } } }
void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const { ProgramStateRef State = C.getState(); // Check if this is the branch for the end of the loop. SVal CollectionSentinel = C.getSVal(FCS); if (CollectionSentinel.isZeroConstant()) { if (!alreadyExecutedAtLeastOneLoopIteration(C.getPredecessor(), FCS)) State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/false); // Otherwise, this is a branch that goes through the loop body. } else { State = checkCollectionNonNil(C, State, FCS); State = checkElementNonNil(C, State, FCS); State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/true); } if (!State) C.generateSink(C.getState(), C.getPredecessor()); else if (State != C.getState()) C.addTransition(State); }
void ExprInspectionChecker::analyzerEval(const CallExpr *CE, CheckerContext &C) const { ExplodedNode *N = C.getPredecessor(); const LocationContext *LC = N->getLocationContext(); // A specific instantiation of an inlined function may have more constrained // values than can generally be assumed. Skip the check. if (LC->getCurrentStackFrame()->getParent() != 0) return; if (!BT) BT.reset(new BugType("Checking analyzer assumptions", "debug")); BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N); C.emitReport(R); }
void DanglingDelegateChecker::verifyIvarDynamicStateAgainstStaticFacts(const Expr &expr, const ObjCIvarDecl *ivarDecl, CheckerContext &context) const { // first retrieve the dangerous 'static' facts we know about the ivar if (!ivarDecl) { return; } const ObjCImplFacts *facts = getCurrentFacts(getCurrentTopClassInterface(context)); if (!facts || facts->_ivarFactsMap.find(ivarDecl) == facts->_ivarFactsMap.end()) { // not an interesting ivar (no entry) return; } const IvarFacts &ivarFacts = facts->_ivarFactsMap.at(ivarDecl); std::string ivarName = ivarDecl->getNameAsString(); // second retrieve the current 'dynamic' state of the ivar ProgramStateRef state = context.getState(); const IvarDynamicState emptyIds; const IvarDynamicState *ids = state->get<IvarMap>(ivarDecl); if (!ids) { ids = &emptyIds; } // Verify that all the dangerous properties have been cleared const StringSet &dangerousProperties = ivarFacts._mayStoreSelfInUnsafeProperty; const StringSet &clearedProperties = ids->_assignPropertyWasCleared; const std::function<void(StringRef)> emitBug([this, &context, &expr](StringRef str) { BugReport *report = new BugReport(*_bugType, str, context.getPredecessor()); report->addRange(expr.getSourceRange()); context.emitReport(report); }); verifyAndReportDangerousProperties(dangerousProperties, clearedProperties, ivarName, ivarDecl->getType(), "", emitBug); // Verify that the object in the ivar is not 'observing' self (TODO) // if (ivarFacts._mayObserveSelf && !ids->_observerWasCleared) { // } // Verify that the object in the ivar is not 'targeting' self (TODO) // if (ivarFacts._mayTargetSelf && !ids->_targetWasCleared) { // } }
void NoReturnFunctionChecker::checkPostCall(const CallEvent &CE, CheckerContext &C) const { bool BuildSinks = false; if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CE.getDecl())) BuildSinks = FD->hasAttr<AnalyzerNoReturnAttr>() || FD->isNoReturn(); const Expr *Callee = CE.getOriginExpr(); if (!BuildSinks && Callee) BuildSinks = getFunctionExtInfo(Callee->getType()).getNoReturn(); if (!BuildSinks && CE.isGlobalCFunction()) { if (const IdentifierInfo *II = CE.getCalleeIdentifier()) { // HACK: Some functions are not marked noreturn, and don't return. // Here are a few hardwired ones. If this takes too long, we can // potentially cache these results. BuildSinks = llvm::StringSwitch<bool>(StringRef(II->getName())) .Case("exit", true) .Case("panic", true) .Case("error", true) .Case("Assert", true) // FIXME: This is just a wrapper around throwing an exception. // Eventually inter-procedural analysis should handle this easily. .Case("ziperr", true) .Case("assfail", true) .Case("db_error", true) .Case("__assert", true) .Case("__assert2", true) // For the purpose of static analysis, we do not care that // this MSVC function will return if the user decides to continue. .Case("_wassert", true) .Case("__assert_rtn", true) .Case("__assert_fail", true) .Case("dtrace_assfail", true) .Case("yy_fatal_error", true) .Case("_XCAssertionFailureHandler", true) .Case("_DTAssertionFailureHandler", true) .Case("_TSAssertionFailureHandler", true) .Default(false); } } if (BuildSinks) C.generateSink(C.getState(), C.getPredecessor()); }
bool CallInliner::EvalCallExpr(CheckerContext &C, const CallExpr *CE) { const GRState *state = C.getState(); const Expr *Callee = CE->getCallee(); SVal L = state->getSVal(Callee); const FunctionDecl *FD = L.getAsFunctionDecl(); if (!FD) return false; if (!FD->getBody(FD)) return false; // Now we have the definition of the callee, create a CallEnter node. CallEnter Loc(CE, FD, C.getPredecessor()->getLocationContext()); C.addTransition(state, Loc); return true; }
void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const { ExplodedNode *N = C.getPredecessor(); const LocationContext *LC = N->getLocationContext(); // An inlined function could conceivably also be analyzed as a top-level // function. We ignore this case and only emit a message (TRUE or FALSE) // when we are analyzing it as an inlined function. This means that // lfort_analyzer_checkInlined(true) should always print TRUE, but // lfort_analyzer_checkInlined(false) should never actually print anything. if (LC->getCurrentStackFrame()->getParent() == 0) return; if (!BT) BT.reset(new BugType("Checking analyzer assumptions", "debug")); BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N); C.emitReport(R); }
/// Cleaning up the program state. void NullabilityChecker::checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const { ProgramStateRef State = C.getState(); NullabilityMapTy Nullabilities = State->get<NullabilityMap>(); for (NullabilityMapTy::iterator I = Nullabilities.begin(), E = Nullabilities.end(); I != E; ++I) { if (!SR.isLiveRegion(I->first)) { State = State->remove<NullabilityMap>(I->first); } } // When one of the nonnull arguments are constrained to be null, nullability // preconditions are violated. It is not enough to check this only when we // actually report an error, because at that time interesting symbols might be // reaped. if (checkPreconditionViolation(State, C.getPredecessor(), C)) return; C.addTransition(State); }
void DanglingDelegateChecker::checkEndFunction(CheckerContext &context) const { // dealloc implicitly releases ivars only in ARC-mode if (!context.getLangOpts().ObjCAutoRefCount) { return; } const ObjCImplFacts *facts = getCurrentFacts(getCurrentTopClassInterface(context)); if (!facts) { return; } const ObjCMethodDecl *decl = dyn_cast_or_null<ObjCMethodDecl>(context.getStackFrame()->getDecl()); if (!decl || decl->getSelector().getAsString() != "dealloc") { return; } const std::function<void(StringRef)> emitBug([this, decl, &context](StringRef str) { BugReport *report = new BugReport(*_bugType, str, context.getPredecessor()); report->addRange(decl->getSourceRange()); context.emitReport(report); }); ProgramStateRef state = context.getState(); // Verify that all the dangerous properties have been cleared const IvarDynamicState emptyIds; for (auto it = facts->_ivarFactsMap.begin(), end = facts->_ivarFactsMap.end(); it != end; it++) { const ObjCIvarDecl *ivarDecl = it->first; const StringSet &dangerousProperties = it->second._mayStoreSelfInUnsafeProperty; const IvarDynamicState *ids = state->get<IvarMap>(ivarDecl); if (!ids) { ids = &emptyIds; } const StringSet &clearedProperties = ids->_assignPropertyWasCleared; verifyAndReportDangerousProperties(dangerousProperties, clearedProperties, ivarDecl->getNameAsString(), ivarDecl->getType(), "ARC-generated code", emitBug); } }
void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const { ProgramStateRef State = C.getState(); AllocatedDataTy ASet = State->get<AllocatedData>(); if (ASet.isEmpty()) return; bool Changed = false; AllocationPairVec Errors; for (AllocatedDataTy::iterator I = ASet.begin(), E = ASet.end(); I != E; ++I) { if (SR.isLive(I->first)) continue; Changed = true; State = State->remove<AllocatedData>(I->first); // If the allocated symbol is null or if the allocation call might have // returned an error, do not report. ConstraintManager &CMgr = State->getConstraintManager(); ConditionTruthVal AllocFailed = CMgr.isNull(State, I.getKey()); if (AllocFailed.isConstrainedTrue() || definitelyReturnedError(I->second.Region, State, C.getSValBuilder())) continue; Errors.push_back(std::make_pair(I->first, &I->second)); } if (!Changed) { // Generate the new, cleaned up state. C.addTransition(State); return; } static SimpleProgramPointTag Tag("MacOSKeychainAPIChecker : DeadSymbolsLeak"); ExplodedNode *N = C.addTransition(C.getState(), C.getPredecessor(), &Tag); // Generate the error reports. for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end(); I != E; ++I) { C.emitReport(generateAllocatedDataNotReleasedReport(*I, N, C)); } // Generate the new, cleaned up state. C.addTransition(State, N); }
void MoveChecker::modelUse(ProgramStateRef State, const MemRegion *Region, const CXXRecordDecl *RD, MisuseKind MK, CheckerContext &C) const { assert(!C.isDifferent() && "No transitions should have been made by now"); const RegionState *RS = State->get<TrackedRegionMap>(Region); ObjectKind OK = classifyObject(Region, RD); // Just in case: if it's not a smart pointer but it does have operator *, // we shouldn't call the bug a dereference. if (MK == MK_Dereference && OK.StdKind != SK_SmartPtr) MK = MK_FunCall; if (!RS || !shouldWarnAbout(OK, MK) || isInMoveSafeContext(C.getLocationContext())) { // Finalize changes made by the caller. C.addTransition(State); return; } // Don't report it in case if any base region is already reported. // But still generate a sink in case of UB. // And still finalize changes made by the caller. if (isAnyBaseRegionReported(State, Region)) { if (misuseCausesCrash(MK)) { C.generateSink(State, C.getPredecessor()); } else { C.addTransition(State); } return; } ExplodedNode *N = reportBug(Region, RD, C, MK); // If the program has already crashed on this path, don't bother. if (N->isSink()) return; State = State->set<TrackedRegionMap>(Region, RegionState::getReported()); C.addTransition(State, N); }
/// Cleaning up the program state. void NullabilityChecker::checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const { if (!SR.hasDeadSymbols()) return; ProgramStateRef State = C.getState(); NullabilityMapTy Nullabilities = State->get<NullabilityMap>(); for (NullabilityMapTy::iterator I = Nullabilities.begin(), E = Nullabilities.end(); I != E; ++I) { const auto *Region = I->first->getAs<SymbolicRegion>(); assert(Region && "Non-symbolic region is tracked."); if (SR.isDead(Region->getSymbol())) { State = State->remove<NullabilityMap>(I->first); } } // When one of the nonnull arguments are constrained to be null, nullability // preconditions are violated. It is not enough to check this only when we // actually report an error, because at that time interesting symbols might be // reaped. if (checkInvariantViolation(State, C.getPredecessor(), C)) return; C.addTransition(State); }
bool OSAtomicChecker::evalOSAtomicCompareAndSwap(CheckerContext &C, const CallExpr *CE) { // Not enough arguments to match OSAtomicCompareAndSwap? if (CE->getNumArgs() != 3) return false; ASTContext &Ctx = C.getASTContext(); const Expr *oldValueExpr = CE->getArg(0); QualType oldValueType = Ctx.getCanonicalType(oldValueExpr->getType()); const Expr *newValueExpr = CE->getArg(1); QualType newValueType = Ctx.getCanonicalType(newValueExpr->getType()); // Do the types of 'oldValue' and 'newValue' match? if (oldValueType != newValueType) return false; const Expr *theValueExpr = CE->getArg(2); const PointerType *theValueType=theValueExpr->getType()->getAs<PointerType>(); // theValueType not a pointer? if (!theValueType) return false; QualType theValueTypePointee = Ctx.getCanonicalType(theValueType->getPointeeType()).getUnqualifiedType(); // The pointee must match newValueType and oldValueType. if (theValueTypePointee != newValueType) return false; static SimpleProgramPointTag OSAtomicLoadTag("OSAtomicChecker : Load"); static SimpleProgramPointTag OSAtomicStoreTag("OSAtomicChecker : Store"); // Load 'theValue'. ExprEngine &Engine = C.getEngine(); const ProgramState *state = C.getState(); ExplodedNodeSet Tmp; SVal location = state->getSVal(theValueExpr); // Here we should use the value type of the region as the load type, because // we are simulating the semantics of the function, not the semantics of // passing argument. So the type of theValue expr is not we are loading. // But usually the type of the varregion is not the type we want either, // we still need to do a CastRetrievedVal in store manager. So actually this // LoadTy specifying can be omitted. But we put it here to emphasize the // semantics. QualType LoadTy; if (const TypedValueRegion *TR = dyn_cast_or_null<TypedValueRegion>(location.getAsRegion())) { LoadTy = TR->getValueType(); } Engine.evalLoad(Tmp, theValueExpr, C.getPredecessor(), state, location, &OSAtomicLoadTag, LoadTy); if (Tmp.empty()) { // If no nodes were generated, other checkers must generated sinks. But // since the builder state was restored, we set it manually to prevent // auto transition. // FIXME: there should be a better approach. C.getNodeBuilder().BuildSinks = true; return true; } for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) { ExplodedNode *N = *I; const ProgramState *stateLoad = N->getState(); // Use direct bindings from the environment since we are forcing a load // from a location that the Environment would typically not be used // to bind a value. SVal theValueVal_untested = stateLoad->getSVal(theValueExpr, true); SVal oldValueVal_untested = stateLoad->getSVal(oldValueExpr); // FIXME: Issue an error. if (theValueVal_untested.isUndef() || oldValueVal_untested.isUndef()) { return false; } DefinedOrUnknownSVal theValueVal = cast<DefinedOrUnknownSVal>(theValueVal_untested); DefinedOrUnknownSVal oldValueVal = cast<DefinedOrUnknownSVal>(oldValueVal_untested); SValBuilder &svalBuilder = Engine.getSValBuilder(); // Perform the comparison. DefinedOrUnknownSVal Cmp = svalBuilder.evalEQ(stateLoad,theValueVal,oldValueVal); const ProgramState *stateEqual = stateLoad->assume(Cmp, true); // Were they equal? if (stateEqual) { // Perform the store. ExplodedNodeSet TmpStore; SVal val = stateEqual->getSVal(newValueExpr); // Handle implicit value casts. if (const TypedValueRegion *R = dyn_cast_or_null<TypedValueRegion>(location.getAsRegion())) { val = svalBuilder.evalCast(val,R->getValueType(), newValueExpr->getType()); } Engine.evalStore(TmpStore, NULL, theValueExpr, N, stateEqual, location, val, &OSAtomicStoreTag); if (TmpStore.empty()) { // If no nodes were generated, other checkers must generated sinks. But // since the builder state was restored, we set it manually to prevent // auto transition. // FIXME: there should be a better approach. C.getNodeBuilder().BuildSinks = true; return true; } // Now bind the result of the comparison. for (ExplodedNodeSet::iterator I2 = TmpStore.begin(), E2 = TmpStore.end(); I2 != E2; ++I2) { ExplodedNode *predNew = *I2; const ProgramState *stateNew = predNew->getState(); // Check for 'void' return type if we have a bogus function prototype. SVal Res = UnknownVal(); QualType T = CE->getType(); if (!T->isVoidType()) Res = Engine.getSValBuilder().makeTruthVal(true, T); C.generateNode(stateNew->BindExpr(CE, Res), predNew); } } // Were they not equal? if (const ProgramState *stateNotEqual = stateLoad->assume(Cmp, false)) { // Check for 'void' return type if we have a bogus function prototype. SVal Res = UnknownVal(); QualType T = CE->getType(); if (!T->isVoidType()) Res = Engine.getSValBuilder().makeTruthVal(false, CE->getType()); C.generateNode(stateNotEqual->BindExpr(CE, Res), N); } } return true; }
void NonNullParamChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (!Call.getDecl()) return; llvm::SmallBitVector AttrNonNull = getNonNullAttrs(Call); unsigned NumArgs = Call.getNumArgs(); ProgramStateRef state = C.getState(); ArrayRef<ParmVarDecl*> parms = Call.parameters(); for (unsigned idx = 0; idx < NumArgs; ++idx) { // For vararg functions, a corresponding parameter decl may not exist. bool HasParam = idx < parms.size(); // Check if the parameter is a reference. We want to report when reference // to a null pointer is passed as a parameter. bool haveRefTypeParam = HasParam ? parms[idx]->getType()->isReferenceType() : false; bool haveAttrNonNull = AttrNonNull[idx]; // Check if the parameter is also marked 'nonnull'. if (!haveAttrNonNull && HasParam) haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>(); if (!haveAttrNonNull && !haveRefTypeParam) continue; // If the value is unknown or undefined, we can't perform this check. const Expr *ArgE = Call.getArgExpr(idx); SVal V = Call.getArgSVal(idx); auto DV = V.getAs<DefinedSVal>(); if (!DV) continue; assert(!haveRefTypeParam || DV->getAs<Loc>()); // Process the case when the argument is not a location. if (haveAttrNonNull && !DV->getAs<Loc>()) { // If the argument is a union type, we want to handle a potential // transparent_union GCC extension. if (!ArgE) continue; QualType T = ArgE->getType(); const RecordType *UT = T->getAsUnionType(); if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>()) continue; auto CSV = DV->getAs<nonloc::CompoundVal>(); // FIXME: Handle LazyCompoundVals? if (!CSV) continue; V = *(CSV->begin()); DV = V.getAs<DefinedSVal>(); assert(++CSV->begin() == CSV->end()); // FIXME: Handle (some_union){ some_other_union_val }, which turns into // a LazyCompoundVal inside a CompoundVal. if (!V.getAs<Loc>()) continue; // Retrieve the corresponding expression. if (const auto *CE = dyn_cast<CompoundLiteralExpr>(ArgE)) if (const auto *IE = dyn_cast<InitListExpr>(CE->getInitializer())) ArgE = dyn_cast<Expr>(*(IE->begin())); } ConstraintManager &CM = C.getConstraintManager(); ProgramStateRef stateNotNull, stateNull; std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); // Generate an error node. Check for a null node in case // we cache out. if (stateNull && !stateNotNull) { if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) { std::unique_ptr<BugReport> R; if (haveAttrNonNull) R = genReportNullAttrNonNull(errorNode, ArgE); else if (haveRefTypeParam) R = genReportReferenceToNullPointer(errorNode, ArgE); // Highlight the range of the argument that was null. R->addRange(Call.getArgSourceRange(idx)); // Emit the bug report. C.emitReport(std::move(R)); } // Always return. Either we cached out or we just emitted an error. return; } if (stateNull) { if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) { ImplicitNullDerefEvent event = { V, false, N, &C.getBugReporter(), /*IsDirectDereference=*/haveRefTypeParam}; dispatchEvent(event); } } // If a pointer value passed the check we should assume that it is // indeed not null from this point forward. state = stateNotNull; } // If we reach here all of the arguments passed the nonnull check. // If 'state' has been updated generated a new node. C.addTransition(state); }
void VLASizeChecker::PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS) { if (!DS->isSingleDecl()) return; const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); if (!VD) return; ASTContext &Ctx = C.getASTContext(); const VariableArrayType *VLA = Ctx.getAsVariableArrayType(VD->getType()); if (!VLA) return; // FIXME: Handle multi-dimensional VLAs. const Expr* SE = VLA->getSizeExpr(); const GRState *state = C.getState(); SVal sizeV = state->getSVal(SE); if (sizeV.isUndef()) { // Generate an error node. ExplodedNode *N = C.generateSink(); if (!N) return; if (!BT_undef) BT_undef = new BuiltinBug("Declared variable-length array (VLA) uses a " "garbage value as its size"); EnhancedBugReport *report = new EnhancedBugReport(*BT_undef, BT_undef->getName(), N); report->addRange(SE->getSourceRange()); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); C.EmitReport(report); return; } // See if the size value is known. It can't be undefined because we would have // warned about that already. if (sizeV.isUnknown()) return; // Check if the size is zero. DefinedSVal sizeD = cast<DefinedSVal>(sizeV); const GRState *stateNotZero, *stateZero; llvm::tie(stateNotZero, stateZero) = state->assume(sizeD); if (stateZero && !stateNotZero) { ExplodedNode* N = C.generateSink(stateZero); if (!BT_zero) BT_zero = new BuiltinBug("Declared variable-length array (VLA) has zero " "size"); EnhancedBugReport *report = new EnhancedBugReport(*BT_zero, BT_zero->getName(), N); report->addRange(SE->getSourceRange()); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); C.EmitReport(report); return; } // From this point on, assume that the size is not zero. state = stateNotZero; // VLASizeChecker is responsible for defining the extent of the array being // declared. We do this by multiplying the array length by the element size, // then matching that with the array region's extent symbol. // Convert the array length to size_t. SValBuilder &svalBuilder = C.getSValBuilder(); QualType SizeTy = Ctx.getSizeType(); NonLoc ArrayLength = cast<NonLoc>(svalBuilder.evalCast(sizeD, SizeTy, SE->getType())); // Get the element size. CharUnits EleSize = Ctx.getTypeSizeInChars(VLA->getElementType()); SVal EleSizeVal = svalBuilder.makeIntVal(EleSize.getQuantity(), SizeTy); // Multiply the array length by the element size. SVal ArraySizeVal = svalBuilder.evalBinOpNN(state, BO_Mul, ArrayLength, cast<NonLoc>(EleSizeVal), SizeTy); // Finally, assume that the array's extent matches the given size. const LocationContext *LC = C.getPredecessor()->getLocationContext(); DefinedOrUnknownSVal Extent = state->getRegion(VD, LC)->getExtent(svalBuilder); DefinedOrUnknownSVal ArraySize = cast<DefinedOrUnknownSVal>(ArraySizeVal); DefinedOrUnknownSVal sizeIsKnown = svalBuilder.evalEQ(state, Extent, ArraySize); state = state->assume(sizeIsKnown, true); // Assume should not fail at this point. assert(state); // Remember our assumptions! C.addTransition(state); }
bool BuiltinFunctionChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { ProgramStateRef state = C.getState(); const FunctionDecl *FD = C.getCalleeDecl(CE); const LocationContext *LCtx = C.getLocationContext(); if (!FD) return false; switch (FD->getBuiltinID()) { default: return false; case Builtin::BI__builtin_assume: { assert (CE->arg_begin() != CE->arg_end()); SVal ArgSVal = C.getSVal(CE->getArg(0)); if (ArgSVal.isUndef()) return true; // Return true to model purity. state = state->assume(ArgSVal.castAs<DefinedOrUnknownSVal>(), true); // FIXME: do we want to warn here? Not right now. The most reports might // come from infeasible paths, thus being false positives. if (!state) { C.generateSink(C.getState(), C.getPredecessor()); return true; } C.addTransition(state); return true; } case Builtin::BI__builtin_unpredictable: case Builtin::BI__builtin_expect: case Builtin::BI__builtin_assume_aligned: case Builtin::BI__builtin_addressof: { // For __builtin_unpredictable, __builtin_expect, and // __builtin_assume_aligned, just return the value of the subexpression. // __builtin_addressof is going from a reference to a pointer, but those // are represented the same way in the analyzer. assert (CE->arg_begin() != CE->arg_end()); SVal X = C.getSVal(*(CE->arg_begin())); C.addTransition(state->BindExpr(CE, LCtx, X)); return true; } case Builtin::BI__builtin_alloca_with_align: case Builtin::BI__builtin_alloca: { // FIXME: Refactor into StoreManager itself? MemRegionManager& RM = C.getStoreManager().getRegionManager(); const AllocaRegion* R = RM.getAllocaRegion(CE, C.blockCount(), C.getLocationContext()); // Set the extent of the region in bytes. This enables us to use the // SVal of the argument directly. If we save the extent in bits, we // cannot represent values like symbol*8. auto Size = C.getSVal(*(CE->arg_begin())).castAs<DefinedOrUnknownSVal>(); SValBuilder& svalBuilder = C.getSValBuilder(); DefinedOrUnknownSVal Extent = R->getExtent(svalBuilder); DefinedOrUnknownSVal extentMatchesSizeArg = svalBuilder.evalEQ(state, Extent, Size); state = state->assume(extentMatchesSizeArg, true); assert(state && "The region should not have any previous constraints"); C.addTransition(state->BindExpr(CE, LCtx, loc::MemRegionVal(R))); return true; } case Builtin::BI__builtin_object_size: { // This must be resolvable at compile time, so we defer to the constant // evaluator for a value. SVal V = UnknownVal(); llvm::APSInt Result; if (CE->EvaluateAsInt(Result, C.getASTContext(), Expr::SE_NoSideEffects)) { // Make sure the result has the correct type. SValBuilder &SVB = C.getSValBuilder(); BasicValueFactory &BVF = SVB.getBasicValueFactory(); BVF.getAPSIntType(CE->getType()).apply(Result); V = SVB.makeIntVal(Result); } C.addTransition(state->BindExpr(CE, LCtx, V)); return true; } } }
/// This callback is used to infer the types for Class variables. This info is /// used later to validate messages that sent to classes. Class variables are /// initialized with by invoking the 'class' method on a class. /// This method is also used to infer the type information for the return /// types. // TODO: right now it only tracks generic types. Extend this to track every // type in the DynamicTypeMap and diagnose type errors! void DynamicTypePropagation::checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const { const ObjCMessageExpr *MessageExpr = M.getOriginExpr(); SymbolRef RetSym = M.getReturnValue().getAsSymbol(); if (!RetSym) return; Selector Sel = MessageExpr->getSelector(); ProgramStateRef State = C.getState(); // Inference for class variables. // We are only interested in cases where the class method is invoked on a // class. This method is provided by the runtime and available on all classes. if (MessageExpr->getReceiverKind() == ObjCMessageExpr::Class && Sel.getAsString() == "class") { QualType ReceiverType = MessageExpr->getClassReceiver(); const auto *ReceiverClassType = ReceiverType->getAs<ObjCObjectType>(); QualType ReceiverClassPointerType = C.getASTContext().getObjCObjectPointerType( QualType(ReceiverClassType, 0)); if (!ReceiverClassType->isSpecialized()) return; const auto *InferredType = ReceiverClassPointerType->getAs<ObjCObjectPointerType>(); assert(InferredType); State = State->set<MostSpecializedTypeArgsMap>(RetSym, InferredType); C.addTransition(State); return; } // Tracking for return types. SymbolRef RecSym = M.getReceiverSVal().getAsSymbol(); if (!RecSym) return; const ObjCObjectPointerType *const *TrackedType = State->get<MostSpecializedTypeArgsMap>(RecSym); if (!TrackedType) return; ASTContext &ASTCtxt = C.getASTContext(); const ObjCMethodDecl *Method = findMethodDecl(MessageExpr, *TrackedType, ASTCtxt); if (!Method) return; Optional<ArrayRef<QualType>> TypeArgs = (*TrackedType)->getObjCSubstitutions(Method->getDeclContext()); if (!TypeArgs) return; QualType ResultType = getReturnTypeForMethod(Method, *TypeArgs, *TrackedType, ASTCtxt); // The static type is the same as the deduced type. if (ResultType.isNull()) return; const MemRegion *RetRegion = M.getReturnValue().getAsRegion(); ExplodedNode *Pred = C.getPredecessor(); // When there is an entry available for the return symbol in DynamicTypeMap, // the call was inlined, and the information in the DynamicTypeMap is should // be precise. if (RetRegion && !State->get<DynamicTypeMap>(RetRegion)) { // TODO: we have duplicated information in DynamicTypeMap and // MostSpecializedTypeArgsMap. We should only store anything in the later if // the stored data differs from the one stored in the former. State = setDynamicTypeInfo(State, RetRegion, ResultType, /*CanBeSubclass=*/true); Pred = C.addTransition(State); } const auto *ResultPtrType = ResultType->getAs<ObjCObjectPointerType>(); if (!ResultPtrType || ResultPtrType->isUnspecialized()) return; // When the result is a specialized type and it is not tracked yet, track it // for the result symbol. if (!State->get<MostSpecializedTypeArgsMap>(RetSym)) { State = State->set<MostSpecializedTypeArgsMap>(RetSym, ResultPtrType); C.addTransition(State, Pred); } }
/// Propagate the nullability information through binds and warn when nullable /// pointer or null symbol is assigned to a pointer with a nonnull type. void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const { const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(L.getAsRegion()); if (!TVR) return; QualType LocType = TVR->getValueType(); if (!LocType->isAnyPointerType()) return; ProgramStateRef State = C.getState(); if (State->get<PreconditionViolated>()) return; auto ValDefOrUnknown = V.getAs<DefinedOrUnknownSVal>(); if (!ValDefOrUnknown) return; NullConstraint RhsNullness = getNullConstraint(*ValDefOrUnknown, State); Nullability ValNullability = Nullability::Unspecified; if (SymbolRef Sym = ValDefOrUnknown->getAsSymbol()) ValNullability = getNullabilityAnnotation(Sym->getType()); Nullability LocNullability = getNullabilityAnnotation(LocType); if (Filter.CheckNullPassedToNonnull && RhsNullness == NullConstraint::IsNull && ValNullability != Nullability::Nonnull && LocNullability == Nullability::Nonnull) { static CheckerProgramPointTag Tag(this, "NullPassedToNonnull"); ExplodedNode *N = C.generateErrorNode(State, &Tag); if (!N) return; const Stmt *ValueExpr = matchValueExprForBind(S); if (!ValueExpr) ValueExpr = S; reportBugIfPreconditionHolds(ErrorKind::NilAssignedToNonnull, N, nullptr, C, ValueExpr); return; } // Intentionally missing case: '0' is bound to a reference. It is handled by // the DereferenceChecker. const MemRegion *ValueRegion = getTrackRegion(*ValDefOrUnknown); if (!ValueRegion) return; const NullabilityState *TrackedNullability = State->get<NullabilityMap>(ValueRegion); if (TrackedNullability) { if (RhsNullness == NullConstraint::IsNotNull || TrackedNullability->getValue() != Nullability::Nullable) return; if (Filter.CheckNullablePassedToNonnull && LocNullability == Nullability::Nonnull) { static CheckerProgramPointTag Tag(this, "NullablePassedToNonnull"); ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag); reportBugIfPreconditionHolds(ErrorKind::NullableAssignedToNonnull, N, ValueRegion, C); } return; } const auto *BinOp = dyn_cast<BinaryOperator>(S); if (ValNullability == Nullability::Nullable) { // Trust the static information of the value more than the static // information on the location. const Stmt *NullabilitySource = BinOp ? BinOp->getRHS() : S; State = State->set<NullabilityMap>( ValueRegion, NullabilityState(ValNullability, NullabilitySource)); C.addTransition(State); return; } if (LocNullability == Nullability::Nullable) { const Stmt *NullabilitySource = BinOp ? BinOp->getLHS() : S; State = State->set<NullabilityMap>( ValueRegion, NullabilityState(LocNullability, NullabilitySource)); C.addTransition(State); } }
/// Propagate the nullability information through binds and warn when nullable /// pointer or null symbol is assigned to a pointer with a nonnull type. void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const { const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(L.getAsRegion()); if (!TVR) return; QualType LocType = TVR->getValueType(); if (!LocType->isAnyPointerType()) return; ProgramStateRef State = C.getState(); if (State->get<InvariantViolated>()) return; auto ValDefOrUnknown = V.getAs<DefinedOrUnknownSVal>(); if (!ValDefOrUnknown) return; NullConstraint RhsNullness = getNullConstraint(*ValDefOrUnknown, State); Nullability ValNullability = Nullability::Unspecified; if (SymbolRef Sym = ValDefOrUnknown->getAsSymbol()) ValNullability = getNullabilityAnnotation(Sym->getType()); Nullability LocNullability = getNullabilityAnnotation(LocType); // If the type of the RHS expression is nonnull, don't warn. This // enables explicit suppression with a cast to nonnull. Nullability ValueExprTypeLevelNullability = Nullability::Unspecified; const Expr *ValueExpr = matchValueExprForBind(S); if (ValueExpr) { ValueExprTypeLevelNullability = getNullabilityAnnotation(lookThroughImplicitCasts(ValueExpr)->getType()); } bool NullAssignedToNonNull = (LocNullability == Nullability::Nonnull && RhsNullness == NullConstraint::IsNull); if (Filter.CheckNullPassedToNonnull && NullAssignedToNonNull && ValNullability != Nullability::Nonnull && ValueExprTypeLevelNullability != Nullability::Nonnull && !isARCNilInitializedLocal(C, S)) { static CheckerProgramPointTag Tag(this, "NullPassedToNonnull"); ExplodedNode *N = C.generateErrorNode(State, &Tag); if (!N) return; const Stmt *ValueStmt = S; if (ValueExpr) ValueStmt = ValueExpr; SmallString<256> SBuf; llvm::raw_svector_ostream OS(SBuf); OS << (LocType->isObjCObjectPointerType() ? "nil" : "Null"); OS << " assigned to a pointer which is expected to have non-null value"; reportBugIfInvariantHolds(OS.str(), ErrorKind::NilAssignedToNonnull, N, nullptr, C, ValueStmt); return; } // If null was returned from a non-null function, mark the nullability // invariant as violated even if the diagnostic was suppressed. if (NullAssignedToNonNull) { State = State->set<InvariantViolated>(true); C.addTransition(State); return; } // Intentionally missing case: '0' is bound to a reference. It is handled by // the DereferenceChecker. const MemRegion *ValueRegion = getTrackRegion(*ValDefOrUnknown); if (!ValueRegion) return; const NullabilityState *TrackedNullability = State->get<NullabilityMap>(ValueRegion); if (TrackedNullability) { if (RhsNullness == NullConstraint::IsNotNull || TrackedNullability->getValue() != Nullability::Nullable) return; if (Filter.CheckNullablePassedToNonnull && LocNullability == Nullability::Nonnull) { static CheckerProgramPointTag Tag(this, "NullablePassedToNonnull"); ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag); reportBugIfInvariantHolds("Nullable pointer is assigned to a pointer " "which is expected to have non-null value", ErrorKind::NullableAssignedToNonnull, N, ValueRegion, C); } return; } const auto *BinOp = dyn_cast<BinaryOperator>(S); if (ValNullability == Nullability::Nullable) { // Trust the static information of the value more than the static // information on the location. const Stmt *NullabilitySource = BinOp ? BinOp->getRHS() : S; State = State->set<NullabilityMap>( ValueRegion, NullabilityState(ValNullability, NullabilitySource)); C.addTransition(State); return; } if (LocNullability == Nullability::Nullable) { const Stmt *NullabilitySource = BinOp ? BinOp->getLHS() : S; State = State->set<NullabilityMap>( ValueRegion, NullabilityState(LocNullability, NullabilitySource)); C.addTransition(State); } }
void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, const ProgramState *state, ObjCMessage msg) const { ASTContext &Ctx = C.getASTContext(); // Check the return type of the message expression. A message to nil will // return different values depending on the return type and the architecture. QualType RetTy = msg.getType(Ctx); CanQualType CanRetTy = Ctx.getCanonicalType(RetTy); if (CanRetTy->isStructureOrClassType()) { // FIXME: At some point we shouldn't rely on isConsumedExpr(), but instead // have the "use of undefined value" be smarter about where the // undefined value came from. if (C.getPredecessor()->getParentMap().isConsumedExpr(msg.getOriginExpr())){ if (ExplodedNode *N = C.generateSink(state)) emitNilReceiverBug(C, msg, N); return; } // The result is not consumed by a surrounding expression. Just propagate // the current state. C.addTransition(state); return; } // Other cases: check if the return type is smaller than void*. if (CanRetTy != Ctx.VoidTy && C.getPredecessor()->getParentMap().isConsumedExpr(msg.getOriginExpr())) { // Compute: sizeof(void *) and sizeof(return type) const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy); const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy); if (voidPtrSize < returnTypeSize && !(supportsNilWithFloatRet(Ctx.getTargetInfo().getTriple()) && (Ctx.FloatTy == CanRetTy || Ctx.DoubleTy == CanRetTy || Ctx.LongDoubleTy == CanRetTy || Ctx.LongLongTy == CanRetTy || Ctx.UnsignedLongLongTy == CanRetTy))) { if (ExplodedNode *N = C.generateSink(state)) emitNilReceiverBug(C, msg, N); return; } // Handle the safe cases where the return value is 0 if the // receiver is nil. // // FIXME: For now take the conservative approach that we only // return null values if we *know* that the receiver is nil. // This is because we can have surprises like: // // ... = [[NSScreens screens] objectAtIndex:0]; // // What can happen is that [... screens] could return nil, but // it most likely isn't nil. We should assume the semantics // of this case unless we have *a lot* more knowledge. // SVal V = C.getSValBuilder().makeZeroVal(msg.getType(Ctx)); C.generateNode(state->BindExpr(msg.getOriginExpr(), V)); return; } C.addTransition(state); }
/// This method check when nullable pointer or null value is returned from a /// function that has nonnull return type. /// /// TODO: when nullability preconditons are violated, it is ok to violate the /// nullability postconditons (i.e.: when one of the nonnull parameters are null /// this check should not report any nullability related issue). void NullabilityChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const { auto RetExpr = S->getRetValue(); if (!RetExpr) return; if (!RetExpr->getType()->isAnyPointerType()) return; ProgramStateRef State = C.getState(); if (State->get<PreconditionViolated>()) return; auto RetSVal = State->getSVal(S, C.getLocationContext()).getAs<DefinedOrUnknownSVal>(); if (!RetSVal) return; AnalysisDeclContext *DeclCtxt = C.getLocationContext()->getAnalysisDeclContext(); const FunctionType *FuncType = DeclCtxt->getDecl()->getFunctionType(); if (!FuncType) return; NullConstraint Nullness = getNullConstraint(*RetSVal, State); Nullability StaticNullability = getNullabilityAnnotation(FuncType->getReturnType()); if (Filter.CheckNullReturnedFromNonnull && Nullness == NullConstraint::IsNull && StaticNullability == Nullability::Nonnull) { static CheckerProgramPointTag Tag(this, "NullReturnedFromNonnull"); ExplodedNode *N = C.generateErrorNode(State, &Tag); if (!N) return; reportBugIfPreconditionHolds(ErrorKind::NilReturnedToNonnull, N, nullptr, C, RetExpr); return; } const MemRegion *Region = getTrackRegion(*RetSVal); if (!Region) return; const NullabilityState *TrackedNullability = State->get<NullabilityMap>(Region); if (TrackedNullability) { Nullability TrackedNullabValue = TrackedNullability->getValue(); if (Filter.CheckNullableReturnedFromNonnull && Nullness != NullConstraint::IsNotNull && TrackedNullabValue == Nullability::Nullable && StaticNullability == Nullability::Nonnull) { static CheckerProgramPointTag Tag(this, "NullableReturnedFromNonnull"); ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag); reportBugIfPreconditionHolds(ErrorKind::NullableReturnedToNonnull, N, Region, C); } return; } if (StaticNullability == Nullability::Nullable) { State = State->set<NullabilityMap>(Region, NullabilityState(StaticNullability, S)); C.addTransition(State); } }
/// This method check when nullable pointer or null value is returned from a /// function that has nonnull return type. void NullabilityChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const { auto RetExpr = S->getRetValue(); if (!RetExpr) return; if (!RetExpr->getType()->isAnyPointerType()) return; ProgramStateRef State = C.getState(); if (State->get<InvariantViolated>()) return; auto RetSVal = C.getSVal(S).getAs<DefinedOrUnknownSVal>(); if (!RetSVal) return; bool InSuppressedMethodFamily = false; QualType RequiredRetType; AnalysisDeclContext *DeclCtxt = C.getLocationContext()->getAnalysisDeclContext(); const Decl *D = DeclCtxt->getDecl(); if (auto *MD = dyn_cast<ObjCMethodDecl>(D)) { // HACK: This is a big hammer to avoid warning when there are defensive // nil checks in -init and -copy methods. We should add more sophisticated // logic here to suppress on common defensive idioms but still // warn when there is a likely problem. ObjCMethodFamily Family = MD->getMethodFamily(); if (OMF_init == Family || OMF_copy == Family || OMF_mutableCopy == Family) InSuppressedMethodFamily = true; RequiredRetType = MD->getReturnType(); } else if (auto *FD = dyn_cast<FunctionDecl>(D)) { RequiredRetType = FD->getReturnType(); } else { return; } NullConstraint Nullness = getNullConstraint(*RetSVal, State); Nullability RequiredNullability = getNullabilityAnnotation(RequiredRetType); // If the returned value is null but the type of the expression // generating it is nonnull then we will suppress the diagnostic. // This enables explicit suppression when returning a nil literal in a // function with a _Nonnull return type: // return (NSString * _Nonnull)0; Nullability RetExprTypeLevelNullability = getNullabilityAnnotation(lookThroughImplicitCasts(RetExpr)->getType()); bool NullReturnedFromNonNull = (RequiredNullability == Nullability::Nonnull && Nullness == NullConstraint::IsNull); if (Filter.CheckNullReturnedFromNonnull && NullReturnedFromNonNull && RetExprTypeLevelNullability != Nullability::Nonnull && !InSuppressedMethodFamily && C.getLocationContext()->inTopFrame()) { static CheckerProgramPointTag Tag(this, "NullReturnedFromNonnull"); ExplodedNode *N = C.generateErrorNode(State, &Tag); if (!N) return; SmallString<256> SBuf; llvm::raw_svector_ostream OS(SBuf); OS << (RetExpr->getType()->isObjCObjectPointerType() ? "nil" : "Null"); OS << " returned from a " << C.getDeclDescription(D) << " that is expected to return a non-null value"; reportBugIfInvariantHolds(OS.str(), ErrorKind::NilReturnedToNonnull, N, nullptr, C, RetExpr); return; } // If null was returned from a non-null function, mark the nullability // invariant as violated even if the diagnostic was suppressed. if (NullReturnedFromNonNull) { State = State->set<InvariantViolated>(true); C.addTransition(State); return; } const MemRegion *Region = getTrackRegion(*RetSVal); if (!Region) return; const NullabilityState *TrackedNullability = State->get<NullabilityMap>(Region); if (TrackedNullability) { Nullability TrackedNullabValue = TrackedNullability->getValue(); if (Filter.CheckNullableReturnedFromNonnull && Nullness != NullConstraint::IsNotNull && TrackedNullabValue == Nullability::Nullable && RequiredNullability == Nullability::Nonnull) { static CheckerProgramPointTag Tag(this, "NullableReturnedFromNonnull"); ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag); SmallString<256> SBuf; llvm::raw_svector_ostream OS(SBuf); OS << "Nullable pointer is returned from a " << C.getDeclDescription(D) << " that is expected to return a non-null value"; reportBugIfInvariantHolds(OS.str(), ErrorKind::NullableReturnedToNonnull, N, Region, C); } return; } if (RequiredNullability == Nullability::Nullable) { State = State->set<NullabilityMap>(Region, NullabilityState(RequiredNullability, S)); C.addTransition(State); } }