void CallAndMessageChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); // If this is a call to a C++ method, check if the callee is null or // undefined. if (const CXXInstanceCall *CC = dyn_cast<CXXInstanceCall>(&Call)) { SVal V = CC->getCXXThisVal(); if (V.isUndef()) { if (!BT_cxx_call_undef) BT_cxx_call_undef.reset( new BuiltinBug(this, "Called C++ object pointer is uninitialized")); emitBadCall(BT_cxx_call_undef.get(), C, CC->getCXXThisExpr()); return; } ProgramStateRef StNonNull, StNull; llvm::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>()); if (StNull && !StNonNull) { if (!BT_cxx_call_null) BT_cxx_call_null.reset( new BuiltinBug(this, "Called C++ object pointer is null")); emitBadCall(BT_cxx_call_null.get(), C, CC->getCXXThisExpr()); return; } State = StNonNull; } const Decl *D = Call.getDecl(); if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D)) { // If we have a declaration, we can make sure we pass enough parameters to // the function. unsigned Params = FD->getNumParams(); if (Call.getNumArgs() < Params) { ExplodedNode *N = C.generateSink(); if (!N) return; LazyInit_BT("Function call with too few arguments", BT_call_few_args); SmallString<512> Str; llvm::raw_svector_ostream os(Str); os << "Function taking " << Params << " argument" << (Params == 1 ? "" : "s") << " is called with less (" << Call.getNumArgs() << ")"; BugReport *R = new BugReport(*BT_call_few_args, os.str(), N); C.emitReport(R); } } // Don't check for uninitialized field values in arguments if the // caller has a body that is available and we have the chance to inline it. // This is a hack, but is a reasonable compromise betweens sometimes warning // and sometimes not depending on if we decide to inline a function. const bool checkUninitFields = !(C.getAnalysisManager().shouldInlineCall() && (D && D->getBody())); OwningPtr<BugType> *BT; if (isa<ObjCMethodCall>(Call)) BT = &BT_msg_arg; else BT = &BT_call_arg; for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i) if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i), Call.getArgExpr(i), /*IsFirstArgument=*/i == 0, checkUninitFields, Call, *BT)) return; // If we make it here, record our assumptions about the callee. C.addTransition(State); }
void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { // If the CallExpr doesn't have exactly 1 argument just give up checking. if (CE->getNumArgs() != 1) return; // Get the function declaration of the callee. const ProgramState *state = C.getState(); SVal X = state->getSVal(CE->getCallee()); const FunctionDecl *FD = X.getAsFunctionDecl(); if (!FD) return; if (!BT) { ASTContext &Ctx = C.getASTContext(); Retain = &Ctx.Idents.get("CFRetain"); Release = &Ctx.Idents.get("CFRelease"); BT.reset(new APIMisuse("null passed to CFRetain/CFRelease")); } // Check if we called CFRetain/CFRelease. const IdentifierInfo *FuncII = FD->getIdentifier(); if (!(FuncII == Retain || FuncII == Release)) return; // FIXME: The rest of this just checks that the argument is non-null. // It should probably be refactored and combined with AttrNonNullChecker. // Get the argument's value. const Expr *Arg = CE->getArg(0); SVal ArgVal = state->getSVal(Arg); DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal); if (!DefArgVal) return; // Get a NULL value. SValBuilder &svalBuilder = C.getSValBuilder(); DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType())); // Make an expression asserting that they're equal. DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal); // Are they equal? const ProgramState *stateTrue, *stateFalse; llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull); if (stateTrue && !stateFalse) { ExplodedNode *N = C.generateSink(stateTrue); if (!N) return; const char *description = (FuncII == Retain) ? "Null pointer argument in call to CFRetain" : "Null pointer argument in call to CFRelease"; BugReport *report = new BugReport(*BT, description, N); report->addRange(Arg->getSourceRange()); report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Arg)); C.EmitReport(report); return; } // From here on, we know the argument is non-null. C.addTransition(stateFalse); }
/// 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 MoveChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); // Remove the MemRegions from the map on which a ctor/dtor call or assignment // happened. // Checking constructor calls. if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) { State = removeFromState(State, CC->getCXXThisVal().getAsRegion()); auto CtorDec = CC->getDecl(); // Check for copying a moved-from object and report the bug. if (CtorDec && CtorDec->isCopyOrMoveConstructor()) { const MemRegion *ArgRegion = CC->getArgSVal(0).getAsRegion(); const CXXRecordDecl *RD = CtorDec->getParent(); MisuseKind MK = CtorDec->isMoveConstructor() ? MK_Move : MK_Copy; modelUse(State, ArgRegion, RD, MK, C); return; } } const auto IC = dyn_cast<CXXInstanceCall>(&Call); if (!IC) return; // Calling a destructor on a moved object is fine. if (isa<CXXDestructorCall>(IC)) return; const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); if (!ThisRegion) return; // The remaining part is check only for method call on a moved-from object. const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(IC->getDecl()); if (!MethodDecl) return; // We want to investigate the whole object, not only sub-object of a parent // class in which the encountered method defined. ThisRegion = ThisRegion->getMostDerivedObjectRegion(); if (isStateResetMethod(MethodDecl)) { State = removeFromState(State, ThisRegion); C.addTransition(State); return; } if (isMoveSafeMethod(MethodDecl)) return; // Store class declaration as well, for bug reporting purposes. const CXXRecordDecl *RD = MethodDecl->getParent(); if (MethodDecl->isOverloadedOperator()) { OverloadedOperatorKind OOK = MethodDecl->getOverloadedOperator(); if (OOK == OO_Equal) { // Remove the tracked object for every assignment operator, but report bug // only for move or copy assignment's argument. State = removeFromState(State, ThisRegion); if (MethodDecl->isCopyAssignmentOperator() || MethodDecl->isMoveAssignmentOperator()) { const MemRegion *ArgRegion = IC->getArgSVal(0).getAsRegion(); MisuseKind MK = MethodDecl->isMoveAssignmentOperator() ? MK_Move : MK_Copy; modelUse(State, ArgRegion, RD, MK, C); return; } C.addTransition(State); return; } if (OOK == OO_Star || OOK == OO_Arrow) { modelUse(State, ThisRegion, RD, MK_Dereference, C); return; } } modelUse(State, ThisRegion, RD, MK_FunCall, C); }
void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { ProgramStateRef state = C.getState(); const FunctionDecl *FD = C.getCalleeDecl(CE); 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. const LocationContext *LCtx = C.getLocationContext(); SVal TheTypeVal = state->getSVal(CE->getArg(1), LCtx); // FIXME: We really should allow ranges of valid theType values, and // bifurcate the state appropriately. Optional<nonloc::ConcreteInt> V = TheTypeVal.getAs<nonloc::ConcreteInt>(); if (!V) return; uint64_t NumberKind = V->getValue().getLimitedValue(); Optional<uint64_t> OptTargetSize = GetCFNumberSize(Ctx, NumberKind); // FIXME: In some cases we can emit an error. if (!OptTargetSize) return; uint64_t TargetSize = *OptTargetSize; // 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), LCtx); // FIXME: Eventually we should handle arbitrary locations. We can do this // by having an enhanced memory model that does low-level typing. Optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>(); if (!LV) return; const TypedValueRegion* R = dyn_cast<TypedValueRegion>(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->isIntegralOrEnumerationType()) 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.addTransition()) { 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.reset(new APIMisuse("Bad use of CFNumberCreate")); BugReport *report = new BugReport(*BT, os.str(), N); report->addRange(CE->getArg(2)->getSourceRange()); C.emitReport(report); } }
void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &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); orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", 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. Optional<ExplodedNode*> errorNode; ProgramStateRef state = C.getState(); for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { QualType ArgTy = msg.getArgExpr(I)->getType(); if (ArgTy->isObjCObjectPointerType()) continue; // Block pointers are treaded as Objective-C pointers. if (ArgTy->isBlockPointerType()) continue; // Ignore pointer constants. if (msg.getArgSVal(I).getAs<loc::ConcreteInt>()) 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.addTransition(); if (!errorNode.getValue()) continue; SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); StringRef TypeName = GetReceiverInterfaceName(msg); if (!TypeName.empty()) os << "Argument to '" << TypeName << "' method '"; else os << "Argument to method '"; os << msg.getSelector().getAsString() << "' should be an Objective-C pointer type, not '"; ArgTy.print(os, C.getLangOpts()); os << "'"; BugReport *R = new BugReport(*BT, os.str(), errorNode.getValue()); R->addRange(msg.getArgSourceRange(I)); C.emitReport(R); } }
/// Calculate the nullability of the result of a message expr based on the /// nullability of the receiver, the nullability of the return value, and the /// constraints. void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const { auto Decl = M.getDecl(); if (!Decl) return; QualType RetType = Decl->getReturnType(); if (!RetType->isAnyPointerType()) return; ProgramStateRef State = C.getState(); if (State->get<PreconditionViolated>()) return; const MemRegion *ReturnRegion = getTrackRegion(M.getReturnValue()); if (!ReturnRegion) return; auto Interface = Decl->getClassInterface(); auto Name = Interface ? Interface->getName() : ""; // In order to reduce the noise in the diagnostics generated by this checker, // some framework and programming style based heuristics are used. These // heuristics are for Cocoa APIs which have NS prefix. if (Name.startswith("NS")) { // Developers rely on dynamic invariants such as an item should be available // in a collection, or a collection is not empty often. Those invariants can // not be inferred by any static analysis tool. To not to bother the users // with too many false positives, every item retrieval function should be // ignored for collections. The instance methods of dictionaries in Cocoa // are either item retrieval related or not interesting nullability wise. // Using this fact, to keep the code easier to read just ignore the return // value of every instance method of dictionaries. if (M.isInstanceMessage() && Name.find("Dictionary") != StringRef::npos) { State = State->set<NullabilityMap>(ReturnRegion, Nullability::Contradicted); C.addTransition(State); return; } // For similar reasons ignore some methods of Cocoa arrays. StringRef FirstSelectorSlot = M.getSelector().getNameForSlot(0); if (Name.find("Array") != StringRef::npos && (FirstSelectorSlot == "firstObject" || FirstSelectorSlot == "lastObject")) { State = State->set<NullabilityMap>(ReturnRegion, Nullability::Contradicted); C.addTransition(State); return; } // Encoding related methods of string should not fail when lossless // encodings are used. Using lossless encodings is so frequent that ignoring // this class of methods reduced the emitted diagnostics by about 30% on // some projects (and all of that was false positives). if (Name.find("String") != StringRef::npos) { for (auto Param : M.parameters()) { if (Param->getName() == "encoding") { State = State->set<NullabilityMap>(ReturnRegion, Nullability::Contradicted); C.addTransition(State); return; } } } } const ObjCMessageExpr *Message = M.getOriginExpr(); Nullability SelfNullability = getReceiverNullability(M, State); const NullabilityState *NullabilityOfReturn = State->get<NullabilityMap>(ReturnRegion); if (NullabilityOfReturn) { // When we have a nullability tracked for the return value, the nullability // of the expression will be the most nullable of the receiver and the // return value. Nullability RetValTracked = NullabilityOfReturn->getValue(); Nullability ComputedNullab = getMostNullable(RetValTracked, SelfNullability); if (ComputedNullab != RetValTracked && ComputedNullab != Nullability::Unspecified) { const Stmt *NullabilitySource = ComputedNullab == RetValTracked ? NullabilityOfReturn->getNullabilitySource() : Message->getInstanceReceiver(); State = State->set<NullabilityMap>( ReturnRegion, NullabilityState(ComputedNullab, NullabilitySource)); C.addTransition(State); } return; } // No tracked information. Use static type information for return value. Nullability RetNullability = getNullabilityAnnotation(RetType); // Properties might be computed. For this reason the static analyzer creates a // new symbol each time an unknown property is read. To avoid false pozitives // do not treat unknown properties as nullable, even when they explicitly // marked nullable. if (M.getMessageKind() == OCM_PropertyAccess && !C.wasInlined) RetNullability = Nullability::Nonnull; Nullability ComputedNullab = getMostNullable(RetNullability, SelfNullability); if (ComputedNullab == Nullability::Nullable) { const Stmt *NullabilitySource = ComputedNullab == RetNullability ? Message : Message->getInstanceReceiver(); State = State->set<NullabilityMap>( ReturnRegion, NullabilityState(ComputedNullab, NullabilitySource)); C.addTransition(State); } }
void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) const { const ProgramState *state = CheckDoubleClose(CE, C.getState(), C); if (state) 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.generateSink(State, C.getPredecessor(), &Tag); 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 callback warns when a nullable pointer or a null value is passed to a /// function that expects its argument to be nonnull. void NullabilityChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (!Call.getDecl()) return; ProgramStateRef State = C.getState(); if (State->get<PreconditionViolated>()) return; ProgramStateRef OrigState = State; unsigned Idx = 0; for (const ParmVarDecl *Param : Call.parameters()) { if (Param->isParameterPack()) break; const Expr *ArgExpr = nullptr; if (Idx < Call.getNumArgs()) ArgExpr = Call.getArgExpr(Idx); auto ArgSVal = Call.getArgSVal(Idx++).getAs<DefinedOrUnknownSVal>(); if (!ArgSVal) continue; if (!Param->getType()->isAnyPointerType() && !Param->getType()->isReferenceType()) continue; NullConstraint Nullness = getNullConstraint(*ArgSVal, State); Nullability ParamNullability = getNullabilityAnnotation(Param->getType()); Nullability ArgStaticNullability = getNullabilityAnnotation(ArgExpr->getType()); if (Filter.CheckNullPassedToNonnull && Nullness == NullConstraint::IsNull && ArgStaticNullability != Nullability::Nonnull && ParamNullability == Nullability::Nonnull) { ExplodedNode *N = C.generateSink(State); reportBugIfPreconditionHolds(ErrorKind::NilPassedToNonnull, N, nullptr, C, ArgExpr); return; } const MemRegion *Region = getTrackRegion(*ArgSVal); if (!Region) continue; const NullabilityState *TrackedNullability = State->get<NullabilityMap>(Region); if (TrackedNullability) { if (Nullness == NullConstraint::IsNotNull || TrackedNullability->getValue() != Nullability::Nullable) continue; if (Filter.CheckNullablePassedToNonnull && ParamNullability == Nullability::Nonnull) { ExplodedNode *N = C.addTransition(State); reportBugIfPreconditionHolds(ErrorKind::NullablePassedToNonnull, N, Region, C, ArgExpr, /*SuppressPath=*/true); return; } if (Filter.CheckNullableDereferenced && Param->getType()->isReferenceType()) { ExplodedNode *N = C.addTransition(State); reportBugIfPreconditionHolds(ErrorKind::NullableDereferenced, N, Region, C, ArgExpr, /*SuppressPath=*/true); return; } continue; } // No tracked nullability yet. if (ArgStaticNullability != Nullability::Nullable) continue; State = State->set<NullabilityMap>( Region, NullabilityState(ArgStaticNullability, ArgExpr)); } if (State != OrigState) C.addTransition(State); }
static void setFlag(ProgramStateRef state, SVal val, CheckerContext &C) { // We tag the symbol that the SVal wraps. if (SymbolRef sym = val.getAsSymbol()) C.addTransition(state->set<T>(sym, true)); }
/// This callback warns when a nullable pointer or a null value is passed to a /// function that expects its argument to be nonnull. void NullabilityChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (!Call.getDecl()) return; ProgramStateRef State = C.getState(); if (State->get<InvariantViolated>()) return; ProgramStateRef OrigState = State; unsigned Idx = 0; for (const ParmVarDecl *Param : Call.parameters()) { if (Param->isParameterPack()) break; if (Idx >= Call.getNumArgs()) break; const Expr *ArgExpr = Call.getArgExpr(Idx); auto ArgSVal = Call.getArgSVal(Idx++).getAs<DefinedOrUnknownSVal>(); if (!ArgSVal) continue; if (!Param->getType()->isAnyPointerType() && !Param->getType()->isReferenceType()) continue; NullConstraint Nullness = getNullConstraint(*ArgSVal, State); Nullability RequiredNullability = getNullabilityAnnotation(Param->getType()); Nullability ArgExprTypeLevelNullability = getNullabilityAnnotation(ArgExpr->getType()); unsigned ParamIdx = Param->getFunctionScopeIndex() + 1; if (Filter.CheckNullPassedToNonnull && Nullness == NullConstraint::IsNull && ArgExprTypeLevelNullability != Nullability::Nonnull && RequiredNullability == Nullability::Nonnull && isDiagnosableCall(Call)) { ExplodedNode *N = C.generateErrorNode(State); if (!N) return; SmallString<256> SBuf; llvm::raw_svector_ostream OS(SBuf); OS << (Param->getType()->isObjCObjectPointerType() ? "nil" : "Null"); OS << " passed to a callee that requires a non-null " << ParamIdx << llvm::getOrdinalSuffix(ParamIdx) << " parameter"; reportBugIfInvariantHolds(OS.str(), ErrorKind::NilPassedToNonnull, N, nullptr, C, ArgExpr, /*SuppressPath=*/false); return; } const MemRegion *Region = getTrackRegion(*ArgSVal); if (!Region) continue; const NullabilityState *TrackedNullability = State->get<NullabilityMap>(Region); if (TrackedNullability) { if (Nullness == NullConstraint::IsNotNull || TrackedNullability->getValue() != Nullability::Nullable) continue; if (Filter.CheckNullablePassedToNonnull && RequiredNullability == Nullability::Nonnull && isDiagnosableCall(Call)) { ExplodedNode *N = C.addTransition(State); SmallString<256> SBuf; llvm::raw_svector_ostream OS(SBuf); OS << "Nullable pointer is passed to a callee that requires a non-null " << ParamIdx << llvm::getOrdinalSuffix(ParamIdx) << " parameter"; reportBugIfInvariantHolds(OS.str(), ErrorKind::NullablePassedToNonnull, N, Region, C, ArgExpr, /*SuppressPath=*/true); return; } if (Filter.CheckNullableDereferenced && Param->getType()->isReferenceType()) { ExplodedNode *N = C.addTransition(State); reportBugIfInvariantHolds("Nullable pointer is dereferenced", ErrorKind::NullableDereferenced, N, Region, C, ArgExpr, /*SuppressPath=*/true); return; } continue; } // No tracked nullability yet. if (ArgExprTypeLevelNullability != Nullability::Nullable) continue; State = State->set<NullabilityMap>( Region, NullabilityState(ArgExprTypeLevelNullability, ArgExpr)); } if (State != OrigState) 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); } }
void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, SVal lock, bool isTryLock, enum LockingSemantics semantics) const { const MemRegion *lockR = lock.getAsRegion(); if (!lockR) return; ProgramStateRef state = C.getState(); const SymbolRef *sym = state->get<DestroyRetVal>(lockR); if (sym) state = resolvePossiblyDestroyedMutex(state, lockR, sym); SVal X = C.getSVal(CE); if (X.isUnknownOrUndef()) return; DefinedSVal retVal = X.castAs<DefinedSVal>(); if (const LockState *LState = state->get<LockMap>(lockR)) { if (LState->isLocked()) { if (!BT_doublelock) BT_doublelock.reset(new BugType(this, "Double locking", "Lock checker")); ExplodedNode *N = C.generateErrorNode(); if (!N) return; auto report = llvm::make_unique<BugReport>( *BT_doublelock, "This lock has already been acquired", N); report->addRange(CE->getArg(0)->getSourceRange()); C.emitReport(std::move(report)); return; } else if (LState->isDestroyed()) { reportUseDestroyedBug(C, CE); return; } } ProgramStateRef lockSucc = state; if (isTryLock) { // Bifurcate the state, and allow a mode where the lock acquisition fails. ProgramStateRef lockFail; switch (semantics) { case PthreadSemantics: std::tie(lockFail, lockSucc) = state->assume(retVal); break; case XNUSemantics: std::tie(lockSucc, lockFail) = state->assume(retVal); break; default: llvm_unreachable("Unknown tryLock locking semantics"); } assert(lockFail && lockSucc); C.addTransition(lockFail); } else if (semantics == PthreadSemantics) { // Assume that the return value was 0. lockSucc = state->assume(retVal, false); assert(lockSucc); } else { // XNU locking semantics return void on non-try locks assert((semantics == XNUSemantics) && "Unknown locking semantics"); lockSucc = state; } // Record that the lock was acquired. lockSucc = lockSucc->add<LockSet>(lockR); lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked()); C.addTransition(lockSucc); }
/// 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.generateSink(State, C.getPredecessor(), &Tag); reportBugIfPreconditionHolds(ErrorKind::NilAssignedToNonnull, N, nullptr, C, S); 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); } }
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 StackAddrEscapeChecker::checkEndPath(CheckerContext &Ctx) const { ProgramStateRef state = Ctx.getState(); // Iterate over all bindings to global variables and see if it contains // a memory region in the stack space. class CallBack : public StoreManager::BindingsHandler { private: CheckerContext &Ctx; const StackFrameContext *CurSFC; public: SmallVector<std::pair<const MemRegion*, const MemRegion*>, 10> V; CallBack(CheckerContext &CC) : Ctx(CC), CurSFC(CC.getLocationContext()->getCurrentStackFrame()) {} bool HandleBinding(StoreManager &SMgr, Store store, const MemRegion *region, SVal val) { if (!isa<GlobalsSpaceRegion>(region->getMemorySpace())) return true; const MemRegion *vR = val.getAsRegion(); if (!vR) return true; // Under automated retain release, it is okay to assign a block // directly to a global variable. if (Ctx.getASTContext().getLangOpts().ObjCAutoRefCount && isa<BlockDataRegion>(vR)) return true; if (const StackSpaceRegion *SSR = dyn_cast<StackSpaceRegion>(vR->getMemorySpace())) { // If the global variable holds a location in the current stack frame, // record the binding to emit a warning. if (SSR->getStackFrame() == CurSFC) V.push_back(std::make_pair(region, vR)); } return true; } }; CallBack cb(Ctx); state->getStateManager().getStoreManager().iterBindings(state->getStore(),cb); if (cb.V.empty()) return; // Generate an error node. ExplodedNode *N = Ctx.addTransition(state); if (!N) return; if (!BT_stackleak) BT_stackleak.reset( new BuiltinBug("Stack address stored into global variable", "Stack address was saved into a global variable. " "This is dangerous because the address will become " "invalid after returning from the function")); for (unsigned i = 0, e = cb.V.size(); i != e; ++i) { // Generate a report for this bug. SmallString<512> buf; llvm::raw_svector_ostream os(buf); SourceRange range = GenName(os, cb.V[i].second, Ctx.getSourceManager()); os << " is still referred to by the global variable '"; const VarRegion *VR = cast<VarRegion>(cb.V[i].first->getBaseRegion()); os << *VR->getDecl() << "' upon returning to the caller. This will be a dangling reference"; BugReport *report = new BugReport(*BT_stackleak, os.str(), N); if (range.isValid()) report->addRange(range); Ctx.EmitReport(report); } }
void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { unsigned idx = InvalidIdx; ProgramStateRef State = C.getState(); const FunctionDecl *FD = C.getCalleeDecl(CE); if (!FD || FD->getKind() != Decl::Function) return; StringRef funName = C.getCalleeName(FD); if (funName.empty()) return; // If it is a call to an allocator function, it could be a double allocation. idx = getTrackedFunctionIndex(funName, true); if (idx != InvalidIdx) { unsigned paramIdx = FunctionsToTrack[idx].Param; if (CE->getNumArgs() <= paramIdx) return; const Expr *ArgExpr = CE->getArg(paramIdx); if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) if (const AllocationState *AS = State->get<AllocatedData>(V)) { if (!definitelyReturnedError(AS->Region, State, C.getSValBuilder())) { // Remove the value from the state. The new symbol will be added for // tracking when the second allocator is processed in checkPostStmt(). State = State->remove<AllocatedData>(V); ExplodedNode *N = C.addTransition(State); if (!N) return; initBugType(); SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; os << "Allocated data should be released before another call to " << "the allocator: missing a call to '" << FunctionsToTrack[DIdx].Name << "'."; auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N); Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(V)); Report->addRange(ArgExpr->getSourceRange()); Report->markInteresting(AS->Region); C.emitReport(std::move(Report)); } } return; } // Is it a call to one of deallocator functions? idx = getTrackedFunctionIndex(funName, false); if (idx == InvalidIdx) return; unsigned paramIdx = FunctionsToTrack[idx].Param; if (CE->getNumArgs() <= paramIdx) return; // Check the argument to the deallocator. const Expr *ArgExpr = CE->getArg(paramIdx); SVal ArgSVal = State->getSVal(ArgExpr, C.getLocationContext()); // Undef is reported by another checker. if (ArgSVal.isUndef()) return; SymbolRef ArgSM = ArgSVal.getAsLocSymbol(); // If the argument is coming from the heap, globals, or unknown, do not // report it. bool RegionArgIsBad = false; if (!ArgSM) { if (!isBadDeallocationArgument(ArgSVal.getAsRegion())) return; RegionArgIsBad = true; } // Is the argument to the call being tracked? const AllocationState *AS = State->get<AllocatedData>(ArgSM); if (!AS && FunctionsToTrack[idx].Kind != ValidAPI) { return; } // If trying to free data which has not been allocated yet, report as a bug. // TODO: We might want a more precise diagnostic for double free // (that would involve tracking all the freed symbols in the checker state). if (!AS || RegionArgIsBad) { // It is possible that this is a false positive - the argument might // have entered as an enclosing function parameter. if (isEnclosingFunctionParam(ArgExpr)) return; ExplodedNode *N = C.addTransition(State); if (!N) return; initBugType(); auto Report = llvm::make_unique<BugReport>( *BT, "Trying to free data which has not been allocated.", N); Report->addRange(ArgExpr->getSourceRange()); if (AS) Report->markInteresting(AS->Region); C.emitReport(std::move(Report)); return; } // Process functions which might deallocate. if (FunctionsToTrack[idx].Kind == PossibleAPI) { if (funName == "CFStringCreateWithBytesNoCopy") { const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts(); // NULL ~ default deallocator, so warn. if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(), Expr::NPC_ValueDependentIsNotNull)) { const AllocationPair AP = std::make_pair(ArgSM, AS); generateDeallocatorMismatchReport(AP, ArgExpr, C); return; } // One of the default allocators, so warn. if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) { StringRef DeallocatorName = DE->getFoundDecl()->getName(); if (DeallocatorName == "kCFAllocatorDefault" || DeallocatorName == "kCFAllocatorSystemDefault" || DeallocatorName == "kCFAllocatorMalloc") { const AllocationPair AP = std::make_pair(ArgSM, AS); generateDeallocatorMismatchReport(AP, ArgExpr, C); return; } // If kCFAllocatorNull, which does not deallocate, we still have to // find the deallocator. if (DE->getFoundDecl()->getName() == "kCFAllocatorNull") return; } // In all other cases, assume the user supplied a correct deallocator // that will free memory so stop tracking. State = State->remove<AllocatedData>(ArgSM); C.addTransition(State); return; } llvm_unreachable("We know of no other possible APIs."); } // The call is deallocating a value we previously allocated, so remove it // from the next state. State = State->remove<AllocatedData>(ArgSM); // Check if the proper deallocator is used. unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) { const AllocationPair AP = std::make_pair(ArgSM, AS); generateDeallocatorMismatchReport(AP, ArgExpr, C); return; } // If the buffer can be null and the return status can be an error, // report a bad call to free. if (State->assume(ArgSVal.castAs<DefinedSVal>(), false) && !definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) { ExplodedNode *N = C.addTransition(State); if (!N) return; initBugType(); auto Report = llvm::make_unique<BugReport>( *BT, "Only call free if a valid (non-NULL) buffer was returned.", N); Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(ArgSM)); Report->addRange(ArgExpr->getSourceRange()); Report->markInteresting(AS->Region); C.emitReport(std::move(Report)); return; } C.addTransition(State); }
void DanglingDelegateChecker::checkPostObjCMessage(const ObjCMethodCall &message, CheckerContext &context) const { // if the call was inlined, there is nothing else to do if (context.wasInlined) { return; } const ObjCMessageExpr *expr = message.getOriginExpr(); if (!expr) { assert(false); return; } // want an instance message to a non-null receiver const Expr *receiver = expr->getInstanceReceiver(); if (!receiver) { return; } if (isKnownToBeNil(message.getReceiverSVal(), context)) { // we are sure that the receiver is nil => abort mission return; } // retrieves the static facts on ivars const ObjCImplFacts *facts = getCurrentFacts(getCurrentTopClassInterface(context)); if (!facts) { return; } // First we try to detect the setting of an interesting property of self if (isObjCSelfExpr(receiver)) { const ObjCPropertyDecl *propDecl = matchObjCMessageWithPropertySetter(*expr); if (propDecl) { // To mitigate false positives, we verify only setters that have an unknown body. // (Setters with a known body are unfortunately not always inlined.) RuntimeDefinition runtimeDefinition = message.getRuntimeDefinition(); if (!runtimeDefinition.getDecl() || runtimeDefinition.getDecl()->isImplicit()) { verifyIvarDynamicStateAgainstStaticFacts(*expr, propDecl->getPropertyIvarDecl(), context); } // Next we deal with a possible assignment self.x = nil to prevent further warning const ObjCIvarDecl *ivarDecl = propDecl->getPropertyIvarDecl(); if (ivarDecl && facts->_ivarFactsMap.find(ivarDecl) != facts->_ivarFactsMap.end()) { SVal value = message.getArgSVal(0); if (isKnownToBeNil(value, context)) { // mark the corresponding ivar as cleared ProgramStateRef state = context.getState(); IvarDynamicState clearedStateForIvar(facts->_ivarFactsMap.at(ivarDecl)); state = state->set<IvarMap>(ivarDecl, clearedStateForIvar); context.addTransition(state); } } return; } } // What follows detects when we correctly clear the references inside an ivar // This is dual to FactFinder::VisitObjCMessageExpr StringRef selectorStr = expr->getSelector().getAsString(); // do we have a first argument equal to self? bool paramIsSelf = isObjCSelfExpr(getArgOfObjCMessageExpr(*expr, 0)); // is the receiver an interesting ivar? const ObjCIvarDecl *ivarDecl = matchIvarLValueExpression(*receiver); if (ivarDecl && facts->_ivarFactsMap.find(ivarDecl) != facts->_ivarFactsMap.end()) { // is this a release? if (selectorStr == "release" || selectorStr == "autorelease") { assert(!paramIsSelf); verifyIvarDynamicStateAgainstStaticFacts(*expr, ivarDecl, context); return; } // Prepare a new state to modify, associated with the receiver ProgramStateRef state = context.getState(); // Copy the previous state if present IvarDynamicState ivarState(state->get<IvarMap>(ivarDecl)); // is this a setter of an assign property? const ObjCPropertyDecl *propDecl = matchObjCMessageWithPropertySetter(*expr); if (propDecl) { if (propDecl->getSetterKind() != ObjCPropertyDecl::Assign) { return; } std::string propName = propDecl->getNameAsString(); if (!paramIsSelf) { // the property is now considered cleared ivarState._assignPropertyWasCleared.insert(propName); } else { // "unclear" the property since we just stored self again in it ivarState._assignPropertyWasCleared.erase(propName); } } else if (paramIsSelf && selectorStr.startswith("removeTarget:")) { ivarState._targetWasCleared = true; } else if (paramIsSelf && selectorStr.startswith("removeObserver:")) { ivarState._observerWasCleared = true; } else { // return to avoid transitioning to a new identical state return; } // write the new state state = state->set<IvarMap>(ivarDecl, ivarState); context.addTransition(state); return; } // TODO: is the receiver an interesting "observable singleton object"? // string receiverObjectName = matchObservableSingletonObject(receiver); // if (!receiverObjectName.empty()) { // // if (paramIsSelf && selectorStr.startswith("addObserver:")) { // // TODO // } // // } }
void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { // If the CallExpr doesn't have exactly 1 argument just give up checking. if (CE->getNumArgs() != 1) return; ProgramStateRef state = C.getState(); const FunctionDecl *FD = C.getCalleeDecl(CE); if (!FD) return; if (!BT) { ASTContext &Ctx = C.getASTContext(); Retain = &Ctx.Idents.get("CFRetain"); Release = &Ctx.Idents.get("CFRelease"); MakeCollectable = &Ctx.Idents.get("CFMakeCollectable"); BT.reset( new APIMisuse("null passed to CFRetain/CFRelease/CFMakeCollectable")); } // Check if we called CFRetain/CFRelease/CFMakeCollectable. const IdentifierInfo *FuncII = FD->getIdentifier(); if (!(FuncII == Retain || FuncII == Release || FuncII == MakeCollectable)) return; // FIXME: The rest of this just checks that the argument is non-null. // It should probably be refactored and combined with NonNullParamChecker. // Get the argument's value. const Expr *Arg = CE->getArg(0); SVal ArgVal = state->getSVal(Arg, C.getLocationContext()); Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>(); if (!DefArgVal) return; // Get a NULL value. SValBuilder &svalBuilder = C.getSValBuilder(); DefinedSVal zero = svalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>(); // Make an expression asserting that they're equal. DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal); // Are they equal? ProgramStateRef stateTrue, stateFalse; llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull); if (stateTrue && !stateFalse) { ExplodedNode *N = C.generateSink(stateTrue); if (!N) return; const char *description; if (FuncII == Retain) description = "Null pointer argument in call to CFRetain"; else if (FuncII == Release) description = "Null pointer argument in call to CFRelease"; else if (FuncII == MakeCollectable) description = "Null pointer argument in call to CFMakeCollectable"; else llvm_unreachable("impossible case"); BugReport *report = new BugReport(*BT, description, N); report->addRange(Arg->getSourceRange()); bugreporter::trackNullOrUndefValue(N, Arg, *report); C.emitReport(report); return; } // From here on, we know the argument is non-null. C.addTransition(stateFalse); }
void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, const Stmt* LoadS, CheckerContext &checkerContext) const { // NOTE: Instead of using ProgramState::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. ProgramStateRef state = checkerContext.getState(); ProgramStateRef originalState = state; SValBuilder &svalBuilder = checkerContext.getSValBuilder(); const RegionRawOffsetV2 &rawOffset = RegionRawOffsetV2::computeOffset(state, svalBuilder, location); if (!rawOffset.getRegion()) return; // CHECK LOWER BOUND: Is byteOffset < extent begin? // If so, we are doing a load/store // before the first valid offset in the memory region. SVal extentBegin = computeExtentBegin(svalBuilder, rawOffset.getRegion()); if (isa<NonLoc>(extentBegin)) { SVal lowerBound = svalBuilder.evalBinOpNN(state, BO_LT, rawOffset.getByteOffset(), cast<NonLoc>(extentBegin), svalBuilder.getConditionType()); NonLoc *lowerBoundToCheck = dyn_cast<NonLoc>(&lowerBound); if (!lowerBoundToCheck) return; ProgramStateRef 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; ProgramStateRef state_exceedsUpperBound, state_withinUpperBound; llvm::tie(state_exceedsUpperBound, state_withinUpperBound) = state->assume(*upperboundToCheck); // If we are under constrained and the index variables are tainted, report. if (state_exceedsUpperBound && state_withinUpperBound) { if (state->isTainted(rawOffset.getByteOffset())) reportOOB(checkerContext, state_exceedsUpperBound, OOB_Tainted); return; } // If we are constrained enough to definitely exceed the upper bound, report. if (state_exceedsUpperBound) { assert(!state_withinUpperBound); reportOOB(checkerContext, state_exceedsUpperBound, OOB_Excedes); return; } assert(state_withinUpperBound); state = state_withinUpperBound; } while (false); if (state != originalState) checkerContext.addTransition(state); }
// Checking for SecItemAdd and SecItemUpdate, no particular reason for assigning it to PostCall void iOSAppSecLeakingLogsChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { do { if ( !m_pInsecureInstBugType ) { MSEC_DEBUG( "redwud: ", "!m_pInsecureInstBugType" ) ; break ; } initIdentifierInfo( C.getASTContext() ) ; //redwud: Obviously it is what it is if ( !Call.isGlobalCFunction() ) { MSEC_DEBUG( "redwud: ", "!Call.isGlobalCFunction" ) ; break ; } const IdentifierInfo *pCalleeIdent = Call.getCalleeIdentifier() ; unsigned iNumArgs = Call.getNumArgs() ; //We do away with array because it's only two of them and it //will break the while..passing pattern if ( pCalleeIdent != m_piiNSLog ) { if ( (pCalleeIdent != m_piiNSLogv) ) { break ; } //FIXME: Workaround to evade 2nd and onward parameters of NSLogv // Idea: Use identifier for slot from s iNumArgs = 1 ; } CSensitiveInfo &rSenInfo = CSensitiveInfo::create() ; SymbolRef pSymToCheck = NULL ; // Go through each parameter unless find some sensitive info in one of them for ( unsigned iCtr = 0; (iCtr < iNumArgs) && (!pSymToCheck); iCtr++ ) { const Expr *pExpr = Call.getArgExpr( iCtr ) ; StringRef szString ; StringRef szVarName ; CMSecCommon::getStrFromExpr( szString, pExpr, &szVarName ) ; if ( szString.empty() ) // Nil is supported here, so no need to check { // MSEC_DEBUG( "redwud: ", "Empty string" ) ; continue ; } if ( !rSenInfo.isSensitive( szString.str() ) && !rSenInfo.isSensitive( szVarName.str() ) ) { // MSEC_DEBUG( "redwud: ", "!Sensitive :" << szString << "Var name: " << szVarName ) ; continue ; } // Get the symbolic value corresponding to the target parameter. pSymToCheck = Call.getArgSVal( iCtr ).getAsSymbol() ; //Force the issue if ( !pSymToCheck ) { pSymToCheck = CMSecCommon::conjureSymbolRef() ; } } if ( !pSymToCheck ) { break ; } ProgramStateRef pProgState = C.getState() ; //Report this instance CMSecCommon::reportInsecureInstance( pSymToCheck, C, C.addTransition( pProgState ) , *m_pInsecureInstBugType, m_szReportDesc ) ; } while ( _PASSING_ ) ; }
static void addSelfFlag(ProgramStateRef state, SVal val, SelfFlagEnum flag, CheckerContext &C) { // We tag the symbol that the SVal wraps. if (SymbolRef sym = val.getAsSymbol()) C.addTransition(state->set<SelfFlag>(sym, getSelfFlags(val, C) | flag)); }