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 MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const { if (!SymReaper.hasDeadSymbols()) return; const GRState *state = C.getState(); RegionStateTy RS = state->get<RegionState>(); RegionStateTy::Factory &F = state->get_context<RegionState>(); for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { if (SymReaper.isDead(I->first)) { if (I->second.isAllocated()) { if (ExplodedNode *N = C.generateNode()) { if (!BT_Leak) BT_Leak.reset(new BuiltinBug("Memory leak", "Allocated memory never released. Potential memory leak.")); // FIXME: where it is allocated. BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N); C.EmitReport(R); } } // Remove the dead symbol from the map. RS = F.remove(RS, I->first); } } C.generateNode(state->set<RegionState>(RS)); }
void AdjustedReturnValueChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { // Get the result type of the call. QualType expectedResultTy = CE->getType(); // Fetch the signature of the called function. const GRState *state = C.getState(); SVal V = state->getSVal(CE); if (V.isUnknown()) return; // Casting to void? Discard the value. if (expectedResultTy->isVoidType()) { C.generateNode(state->BindExpr(CE, UnknownVal())); return; } const MemRegion *callee = state->getSVal(CE->getCallee()).getAsRegion(); if (!callee) return; QualType actualResultTy; if (const FunctionTextRegion *FT = dyn_cast<FunctionTextRegion>(callee)) { const FunctionDecl *FD = FT->getDecl(); actualResultTy = FD->getResultType(); } else if (const BlockDataRegion *BD = dyn_cast<BlockDataRegion>(callee)) { const BlockTextRegion *BR = BD->getCodeRegion(); const BlockPointerType *BT=BR->getLocationType()->getAs<BlockPointerType>(); const FunctionType *FT = BT->getPointeeType()->getAs<FunctionType>(); actualResultTy = FT->getResultType(); } // Can this happen? if (actualResultTy.isNull()) return; // For now, ignore references. if (actualResultTy->getAs<ReferenceType>()) return; // Are they the same? if (expectedResultTy != actualResultTy) { // FIXME: Do more checking and actual emit an error. At least performing // the cast avoids some assertion failures elsewhere. SValBuilder &svalBuilder = C.getSValBuilder(); V = svalBuilder.evalCast(V, expectedResultTy, actualResultTy); C.generateNode(state->BindExpr(CE, V)); } }
void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const { const GRState *state = C.getState(); if (!(state = CheckNullStream(state->getSVal(CE->getArg(0)), state, C))) return; // Check the legality of the 'whence' argument of 'fseek'. SVal Whence = state->getSVal(CE->getArg(2)); const nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&Whence); if (!CI) return; int64_t x = CI->getValue().getSExtValue(); if (x >= 0 && x <= 2) return; if (ExplodedNode *N = C.generateNode(state)) { if (!BT_illegalwhence) BT_illegalwhence.reset(new BuiltinBug("Illegal whence argument", "The whence argument to fseek() should be " "SEEK_SET, SEEK_END, or SEEK_CUR.")); BugReport *R = new BugReport(*BT_illegalwhence, BT_illegalwhence->getDescription(), N); C.EmitReport(R); } }
void FixedAddressChecker::checkPreStmt(const BinaryOperator *B, CheckerContext &C) const { // Using a fixed address is not portable because that address will probably // not be valid in all environments or platforms. if (B->getOpcode() != BO_Assign) return; QualType T = B->getType(); if (!T->isPointerType()) return; const GRState *state = C.getState(); SVal RV = state->getSVal(B->getRHS()); if (!RV.isConstant() || RV.isZeroConstant()) return; if (ExplodedNode *N = C.generateNode()) { if (!BT) BT.reset(new BuiltinBug("Use fixed address", "Using a fixed address is not portable because that " "address will probably not be valid in all " "environments or platforms.")); RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); R->addRange(B->getRHS()->getSourceRange()); C.EmitReport(R); } }
void ObjCAtSyncChecker::PreVisitObjCAtSynchronizedStmt(CheckerContext &C, const ObjCAtSynchronizedStmt *S) { const Expr *Ex = S->getSynchExpr(); const GRState *state = C.getState(); SVal V = state->getSVal(Ex); // Uninitialized value used for the mutex? if (isa<UndefinedVal>(V)) { if (ExplodedNode *N = C.generateSink()) { if (!BT_undef) BT_undef = new BuiltinBug("Uninitialized value used as mutex " "for @synchronized"); EnhancedBugReport *report = new EnhancedBugReport(*BT_undef, BT_undef->getDescription(), N); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); C.EmitReport(report); } return; } if (V.isUnknown()) return; // Check for null mutexes. const GRState *notNullState, *nullState; llvm::tie(notNullState, nullState) = state->assume(cast<DefinedSVal>(V)); if (nullState) { if (!notNullState) { // Generate an error node. This isn't a sink since // a null mutex just means no synchronization occurs. if (ExplodedNode *N = C.generateNode(nullState)) { if (!BT_null) BT_null = new BuiltinBug("Nil value used as mutex for @synchronized() " "(no synchronization will occur)"); EnhancedBugReport *report = new EnhancedBugReport(*BT_null, BT_null->getDescription(), N); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); C.EmitReport(report); return; } } // Don't add a transition for 'nullState'. If the value is // under-constrained to be null or non-null, assume it is non-null // afterwards. } if (notNullState) C.addTransition(notNullState); }
// Check if the location is a freed symbolic region. void MallocChecker::checkLocation(SVal l, bool isLoad,CheckerContext &C) const { SymbolRef Sym = l.getLocSymbolInBase(); if (Sym) { const RefState *RS = C.getState()->get<RegionState>(Sym); if (RS && RS->isReleased()) { if (ExplodedNode *N = C.generateNode()) { if (!BT_UseFree) BT_UseFree.reset(new BuiltinBug("Use dynamically allocated memory " "after it is freed.")); BugReport *R = new BugReport(*BT_UseFree, BT_UseFree->getDescription(), N); C.EmitReport(R); } } } }
void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, SVal lock) { const MemRegion *lockR = lock.getAsRegion(); if (!lockR) return; const GRState *state = C.getState(); // Record that the lock was released. // FIXME: Handle unlocking locks that were never acquired. This may // require IPA for wrappers. const GRState *unlockState = state->remove<LockSet>(lockR); if (state == unlockState) return; C.addTransition(C.generateNode(CE, unlockState)); }
void CastToStructChecker::checkPreStmt(const CastExpr *CE, CheckerContext &C) const { const Expr *E = CE->getSubExpr(); ASTContext &Ctx = C.getASTContext(); QualType OrigTy = Ctx.getCanonicalType(E->getType()); QualType ToTy = Ctx.getCanonicalType(CE->getType()); const PointerType *OrigPTy = dyn_cast<PointerType>(OrigTy.getTypePtr()); const PointerType *ToPTy = dyn_cast<PointerType>(ToTy.getTypePtr()); if (!ToPTy || !OrigPTy) return; QualType OrigPointeeTy = OrigPTy->getPointeeType(); QualType ToPointeeTy = ToPTy->getPointeeType(); if (!ToPointeeTy->isStructureOrClassType()) return; // We allow cast from void*. if (OrigPointeeTy->isVoidType()) return; // Now the cast-to-type is struct pointer, the original type is not void*. if (!OrigPointeeTy->isRecordType()) { if (ExplodedNode *N = C.generateNode()) { if (!BT) BT.reset(new BuiltinBug("Cast from non-struct type to struct type", "Casting a non-structure type to a structure type " "and accessing a field can lead to memory access " "errors or data corruption.")); RangedBugReport *R = new RangedBugReport(*BT,BT->getDescription(), N); R->addRange(CE->getSourceRange()); C.EmitReport(R); } } }
void ClassReleaseChecker::preVisitObjCMessage(CheckerContext &C, ObjCMessage msg) { if (!BT) { BT = new APIMisuse("message incorrectly sent to class instead of class " "instance"); ASTContext &Ctx = C.getASTContext(); releaseS = GetNullarySelector("release", Ctx); retainS = GetNullarySelector("retain", Ctx); autoreleaseS = GetNullarySelector("autorelease", Ctx); drainS = GetNullarySelector("drain", Ctx); } if (msg.isInstanceMessage()) return; const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); assert(Class); Selector S = msg.getSelector(); if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) return; if (ExplodedNode *N = C.generateNode()) { llvm::SmallString<200> buf; llvm::raw_svector_ostream os(buf); os << "The '" << S.getAsString() << "' message should be sent to instances " "of class '" << Class->getName() << "' and not the class directly"; RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); report->addRange(msg.getSourceRange()); C.EmitReport(report); } }
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); }
void CFNumberCreateChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) { const Expr* Callee = CE->getCallee(); const GRState *state = C.getState(); SVal CallV = state->getSVal(Callee); const FunctionDecl* FD = CallV.getAsFunctionDecl(); if (!FD) return; ASTContext &Ctx = C.getASTContext(); if (!II) II = &Ctx.Idents.get("CFNumberCreate"); if (FD->getIdentifier() != II || CE->getNumArgs() != 3) return; // Get the value of the "theType" argument. SVal TheTypeVal = state->getSVal(CE->getArg(1)); // FIXME: We really should allow ranges of valid theType values, and // bifurcate the state appropriately. nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal); if (!V) return; uint64_t NumberKind = V->getValue().getLimitedValue(); Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind); // FIXME: In some cases we can emit an error. if (!TargetSize.isKnown()) return; // Look at the value of the integer being passed by reference. Essentially // we want to catch cases where the value passed in is not equal to the // size of the type being created. SVal TheValueExpr = state->getSVal(CE->getArg(2)); // FIXME: Eventually we should handle arbitrary locations. We can do this // by having an enhanced memory model that does low-level typing. loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr); if (!LV) return; const TypedRegion* R = dyn_cast<TypedRegion>(LV->StripCasts()); if (!R) return; QualType T = Ctx.getCanonicalType(R->getValueType()); // FIXME: If the pointee isn't an integer type, should we flag a warning? // People can do weird stuff with pointers. if (!T->isIntegerType()) return; uint64_t SourceSize = Ctx.getTypeSize(T); // CHECK: is SourceSize == TargetSize if (SourceSize == TargetSize) return; // Generate an error. Only generate a sink if 'SourceSize < TargetSize'; // otherwise generate a regular node. // // FIXME: We can actually create an abstract "CFNumber" object that has // the bits initialized to the provided values. // if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink() : C.generateNode()) { llvm::SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); os << (SourceSize == 8 ? "An " : "A ") << SourceSize << " bit integer is used to initialize a CFNumber " "object that represents " << (TargetSize == 8 ? "an " : "a ") << TargetSize << " bit integer. "; if (SourceSize < TargetSize) os << (TargetSize - SourceSize) << " bits of the CFNumber value will be garbage." ; else os << (SourceSize - TargetSize) << " bits of the input integer will be lost."; if (!BT) BT = new APIMisuse("Bad use of CFNumberCreate"); RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); report->addRange(CE->getArg(2)->getSourceRange()); C.EmitReport(report); } }
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 ArrayBoundCheckerV2::visitLocation(CheckerContext &checkerContext, const Stmt *S, SVal location, bool isLoad) { // NOTE: Instead of using GRState::assumeInBound(), we are prototyping // some new logic here that reasons directly about memory region extents. // Once that logic is more mature, we can bring it back to assumeInBound() // for all clients to use. // // The algorithm we are using here for bounds checking is to see if the // memory access is within the extent of the base region. Since we // have some flexibility in defining the base region, we can achieve // various levels of conservatism in our buffer overflow checking. const GRState *state = checkerContext.getState(); const GRState *originalState = state; SValBuilder &svalBuilder = checkerContext.getSValBuilder(); const RegionRawOffsetV2 &rawOffset = RegionRawOffsetV2::computeOffset(state, svalBuilder, location); if (!rawOffset.getRegion()) return; // CHECK LOWER BOUND: Is byteOffset < 0? If so, we are doing a load/store // before the first valid offset in the memory region. SVal lowerBound = svalBuilder.evalBinOpNN(state, BO_LT, rawOffset.getByteOffset(), svalBuilder.makeZeroArrayIndex(), svalBuilder.getConditionType()); NonLoc *lowerBoundToCheck = dyn_cast<NonLoc>(&lowerBound); if (!lowerBoundToCheck) return; const GRState *state_precedesLowerBound, *state_withinLowerBound; llvm::tie(state_precedesLowerBound, state_withinLowerBound) = state->assume(*lowerBoundToCheck); // Are we constrained enough to definitely precede the lower bound? if (state_precedesLowerBound && !state_withinLowerBound) { reportOOB(checkerContext, state_precedesLowerBound, OOB_Precedes); return; } // Otherwise, assume the constraint of the lower bound. assert(state_withinLowerBound); state = state_withinLowerBound; do { // CHECK UPPER BOUND: Is byteOffset >= extent(baseRegion)? If so, // we are doing a load/store after the last valid offset. DefinedOrUnknownSVal extentVal = rawOffset.getRegion()->getExtent(svalBuilder); if (!isa<NonLoc>(extentVal)) break; SVal upperbound = svalBuilder.evalBinOpNN(state, BO_GE, rawOffset.getByteOffset(), cast<NonLoc>(extentVal), svalBuilder.getConditionType()); NonLoc *upperboundToCheck = dyn_cast<NonLoc>(&upperbound); if (!upperboundToCheck) break; const GRState *state_exceedsUpperBound, *state_withinUpperBound; llvm::tie(state_exceedsUpperBound, state_withinUpperBound) = state->assume(*upperboundToCheck); // Are we constrained enough to definitely exceed the upper bound? if (state_exceedsUpperBound && !state_withinUpperBound) { reportOOB(checkerContext, state_exceedsUpperBound, OOB_Excedes); return; } assert(state_withinUpperBound); state = state_withinUpperBound; } while (false); if (state != originalState) checkerContext.generateNode(state); }
void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const { if (!BT) { BT.reset(new APIMisuse("Arguments passed to variadic method aren't all " "Objective-C pointer types")); ASTContext &Ctx = C.getASTContext(); arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx); dictionaryWithObjectsAndKeysS = GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx); setWithObjectsS = GetUnarySelector("setWithObjects", Ctx); initWithObjectsS = GetUnarySelector("initWithObjects", Ctx); initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx); } if (!isVariadicMessage(msg)) return; // We are not interested in the selector arguments since they have // well-defined types, so the compiler will issue a warning for them. unsigned variadicArgsBegin = msg.getSelector().getNumArgs(); // We're not interested in the last argument since it has to be nil or the // compiler would have issued a warning for it elsewhere. unsigned variadicArgsEnd = msg.getNumArgs() - 1; if (variadicArgsEnd <= variadicArgsBegin) return; // Verify that all arguments have Objective-C types. llvm::Optional<ExplodedNode*> errorNode; const ProgramState *state = C.getState(); for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { QualType ArgTy = msg.getArgType(I); if (ArgTy->isObjCObjectPointerType()) continue; // Block pointers are treaded as Objective-C pointers. if (ArgTy->isBlockPointerType()) continue; // Ignore pointer constants. if (isa<loc::ConcreteInt>(msg.getArgSVal(I, state))) continue; // Ignore pointer types annotated with 'NSObject' attribute. if (C.getASTContext().isObjCNSObjectType(ArgTy)) continue; // Ignore CF references, which can be toll-free bridged. if (coreFoundation::isCFObjectRef(ArgTy)) continue; // Generate only one error node to use for all bug reports. if (!errorNode.hasValue()) { errorNode = C.generateNode(); } if (!errorNode.getValue()) continue; llvm::SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); if (const char *TypeName = GetReceiverNameType(msg)) os << "Argument to '" << TypeName << "' method '"; else os << "Argument to method '"; os << msg.getSelector().getAsString() << "' should be an Objective-C pointer type, not '" << ArgTy.getAsString() << "'"; BugReport *R = new BugReport(*BT, os.str(), errorNode.getValue()); R->addRange(msg.getArgSourceRange(I)); C.EmitReport(R); } }