void CallAndMessageChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const{ const Expr *Callee = CE->getCallee()->IgnoreParens(); ProgramStateRef State = C.getState(); const LocationContext *LCtx = C.getLocationContext(); SVal L = State->getSVal(Callee, LCtx); if (L.isUndef()) { if (!BT_call_undef) BT_call_undef.reset(new BuiltinBug( this, "Called function pointer is an uninitalized pointer value")); emitBadCall(BT_call_undef.get(), C, Callee); return; } ProgramStateRef StNonNull, StNull; std::tie(StNonNull, StNull) = State->assume(L.castAs<DefinedOrUnknownSVal>()); if (StNull && !StNonNull) { if (!BT_call_null) BT_call_null.reset(new BuiltinBug( this, "Called function pointer is null (null dereference)")); emitBadCall(BT_call_null.get(), C, Callee); return; } C.addTransition(StNonNull); }
/// Assumes that the collection elements are non-nil. /// /// This only applies if the collection is one of those known not to contain /// nil values. static ProgramStateRef checkElementNonNil(CheckerContext &C, ProgramStateRef State, const ObjCForCollectionStmt *FCS) { if (!State) return nullptr; // See if the collection is one where we /know/ the elements are non-nil. if (!isKnownNonNilCollectionType(FCS->getCollection()->getType())) return State; const LocationContext *LCtx = C.getLocationContext(); const Stmt *Element = FCS->getElement(); // FIXME: Copied from ExprEngineObjC. Optional<Loc> ElementLoc; if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) { const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl()); assert(ElemDecl->getInit() == nullptr); ElementLoc = State->getLValue(ElemDecl, LCtx); } else { ElementLoc = State->getSVal(Element, LCtx).getAs<Loc>(); } if (!ElementLoc) return State; // Go ahead and assume the value is non-nil. SVal Val = State->getSVal(*ElementLoc); return State->assume(Val.castAs<DefinedOrUnknownSVal>(), true); }
static const char *getArgumentValueString(const CallExpr *CE, CheckerContext &C) { if (CE->getNumArgs() == 0) return "Missing assertion argument"; ExplodedNode *N = C.getPredecessor(); const LocationContext *LC = N->getLocationContext(); ProgramStateRef State = N->getState(); const Expr *Assertion = CE->getArg(0); SVal AssertionVal = State->getSVal(Assertion, LC); if (AssertionVal.isUndef()) return "UNDEFINED"; ProgramStateRef StTrue, StFalse; llvm::tie(StTrue, StFalse) = State->assume(cast<DefinedOrUnknownSVal>(AssertionVal)); if (StTrue) { if (StFalse) return "UNKNOWN"; else return "TRUE"; } else { if (StFalse) return "FALSE"; else llvm_unreachable("Invalid constraint; neither true or false."); } }
/// Returns NULL state if the collection is known to contain elements /// (or is known not to contain elements if the Assumption parameter is false.) static ProgramStateRef assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State, SymbolRef CollectionS, bool Assumption) { if (!State || !CollectionS) return State; const SymbolRef *CountS = State->get<ContainerCountMap>(CollectionS); if (!CountS) { const bool *KnownNonEmpty = State->get<ContainerNonEmptyMap>(CollectionS); if (!KnownNonEmpty) return State->set<ContainerNonEmptyMap>(CollectionS, Assumption); return (Assumption == *KnownNonEmpty) ? State : nullptr; } SValBuilder &SvalBuilder = C.getSValBuilder(); SVal CountGreaterThanZeroVal = SvalBuilder.evalBinOp(State, BO_GT, nonloc::SymbolVal(*CountS), SvalBuilder.makeIntVal(0, (*CountS)->getType()), SvalBuilder.getConditionType()); Optional<DefinedSVal> CountGreaterThanZero = CountGreaterThanZeroVal.getAs<DefinedSVal>(); if (!CountGreaterThanZero) { // The SValBuilder cannot construct a valid SVal for this condition. // This means we cannot properly reason about it. return State; } return State->assume(*CountGreaterThanZero, Assumption); }
PathDiagnosticPiece *NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR) { Optional<PostStmt> P = N->getLocationAs<PostStmt>(); if (!P) return 0; const ObjCMessageExpr *ME = P->getStmtAs<ObjCMessageExpr>(); if (!ME) return 0; const Expr *Receiver = ME->getInstanceReceiver(); if (!Receiver) return 0; ProgramStateRef state = N->getState(); const SVal &V = state->getSVal(Receiver, N->getLocationContext()); Optional<DefinedOrUnknownSVal> DV = V.getAs<DefinedOrUnknownSVal>(); if (!DV) return 0; state = state->assume(*DV, true); if (state) return 0; // The receiver was nil, and hence the method was skipped. // Register a BugReporterVisitor to issue a message telling us how // the receiver was null. bugreporter::trackNullOrUndefValue(N, Receiver, BR); // Issue a message saying that the method was skipped. PathDiagnosticLocation L(Receiver, BRC.getSourceManager(), N->getLocationContext()); return new PathDiagnosticEventPiece(L, "No method is called " "because the receiver is nil"); }
// Handles casts of type CK_IntegralCast. // At the moment, this function will redirect to evalCast, except when the range // of the original value is known to be greater than the max of the target type. SVal SValBuilder::evalIntegralCast(ProgramStateRef state, SVal val, QualType castTy, QualType originalTy) { // No truncations if target type is big enough. if (getContext().getTypeSize(castTy) >= getContext().getTypeSize(originalTy)) return evalCast(val, castTy, originalTy); const SymExpr *se = val.getAsSymbolicExpression(); if (!se) // Let evalCast handle non symbolic expressions. return evalCast(val, castTy, originalTy); // Find the maximum value of the target type. APSIntType ToType(getContext().getTypeSize(castTy), castTy->isUnsignedIntegerType()); llvm::APSInt ToTypeMax = ToType.getMaxValue(); NonLoc ToTypeMaxVal = makeIntVal(ToTypeMax.isUnsigned() ? ToTypeMax.getZExtValue() : ToTypeMax.getSExtValue(), castTy) .castAs<NonLoc>(); // Check the range of the symbol being casted against the maximum value of the // target type. NonLoc FromVal = val.castAs<NonLoc>(); QualType CmpTy = getConditionType(); NonLoc CompVal = evalBinOpNN(state, BO_LE, FromVal, ToTypeMaxVal, CmpTy).castAs<NonLoc>(); ProgramStateRef IsNotTruncated, IsTruncated; std::tie(IsNotTruncated, IsTruncated) = state->assume(CompVal); if (!IsNotTruncated && IsTruncated) { // Symbol is truncated so we evaluate it as a cast. NonLoc CastVal = makeNonLoc(se, originalTy, castTy); return CastVal; } return evalCast(val, castTy, originalTy); }
/// Returns NULL state if the collection is known to contain elements /// (or is known not to contain elements if the Assumption parameter is false.) static ProgramStateRef assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State, const ObjCForCollectionStmt *FCS, bool Assumption = false) { if (!State) return NULL; SymbolRef CollectionS = C.getSVal(FCS->getCollection()).getAsSymbol(); if (!CollectionS) return State; const SymbolRef *CountS = State->get<ContainerCountMap>(CollectionS); if (!CountS) return State; SValBuilder &SvalBuilder = C.getSValBuilder(); SVal CountGreaterThanZeroVal = SvalBuilder.evalBinOp(State, BO_GT, nonloc::SymbolVal(*CountS), SvalBuilder.makeIntVal(0, (*CountS)->getType()), SvalBuilder.getConditionType()); Optional<DefinedSVal> CountGreaterThanZero = CountGreaterThanZeroVal.getAs<DefinedSVal>(); if (!CountGreaterThanZero) { // The SValBuilder cannot construct a valid SVal for this condition. // This means we cannot properly reason about it. return State; } return State->assume(*CountGreaterThanZero, Assumption); }
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 = State->getSVal(FCS, C.getLocationContext()); if (CollectionSentinel.isZeroConstant()) return; // See if the collection is one where we /know/ the elements are non-nil. const Expr *Collection = FCS->getCollection(); if (!isKnownNonNilCollectionType(Collection->getType())) return; // FIXME: Copied from ExprEngineObjC. const Stmt *Element = FCS->getElement(); SVal ElementVar; if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) { const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl()); assert(ElemDecl->getInit() == 0); ElementVar = State->getLValue(ElemDecl, C.getLocationContext()); } else { ElementVar = State->getSVal(Element, C.getLocationContext()); } if (!ElementVar.getAs<Loc>()) return; // Go ahead and assume the value is non-nil. SVal Val = State->getSVal(ElementVar.castAs<Loc>()); State = State->assume(Val.castAs<DefinedOrUnknownSVal>(), true); C.addTransition(State); }
static ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr, ProgramStateRef State, CheckerContext &C) { SVal Val = State->getSVal(NonNullExpr, C.getLocationContext()); if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>()) return State->assume(*DV, true); return State; }
// Returns true if we try to do a zero byte allocation, false otherwise. // Fills in trueState and falseState. static bool IsZeroByteAllocation(ProgramStateRef state, const SVal argVal, ProgramStateRef *trueState, ProgramStateRef *falseState) { std::tie(*trueState, *falseState) = state->assume(argVal.castAs<DefinedSVal>()); return (*falseState && !*trueState); }
ProgramStateRef ObjCNonNilReturnValueChecker::assumeExprIsNonNull(const Expr *NonNullExpr, ProgramStateRef State, CheckerContext &C) const { SVal Val = C.getSVal(NonNullExpr); if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>()) return State->assume(*DV, true); return State; }
void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { SVal recVal = msg.getReceiverSVal(); if (recVal.isUndef()) { if (ExplodedNode *N = C.generateSink()) { BugType *BT = 0; switch (msg.getMessageKind()) { case OCM_Message: if (!BT_msg_undef) BT_msg_undef.reset(new BuiltinBug("Receiver in message expression " "is an uninitialized value")); BT = BT_msg_undef.get(); break; case OCM_PropertyAccess: if (!BT_objc_prop_undef) BT_objc_prop_undef.reset(new BuiltinBug("Property access on an " "uninitialized object " "pointer")); BT = BT_objc_prop_undef.get(); break; case OCM_Subscript: if (!BT_objc_subscript_undef) BT_objc_subscript_undef.reset(new BuiltinBug("Subscript access on an " "uninitialized object " "pointer")); BT = BT_objc_subscript_undef.get(); break; } assert(BT && "Unknown message kind."); BugReport *R = new BugReport(*BT, BT->getName(), N); const ObjCMessageExpr *ME = msg.getOriginExpr(); R->addRange(ME->getReceiverRange()); // FIXME: getTrackNullOrUndefValueVisitor can't handle "super" yet. if (const Expr *ReceiverE = ME->getInstanceReceiver()) R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, ReceiverE, R)); C.EmitReport(R); } return; } else { // Bifurcate the state into nil and non-nil ones. DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal); ProgramStateRef state = C.getState(); ProgramStateRef notNilState, nilState; llvm::tie(notNilState, nilState) = state->assume(receiverVal); // Handle receiver must be nil. if (nilState && !notNilState) { HandleNilReceiver(C, state, msg); return; } } }
void CallAndMessageChecker::checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const { ProgramStateRef state = C.getState(); const LocationContext *LCtx = C.getLocationContext(); // FIXME: Handle 'super'? if (const Expr *receiver = msg.getInstanceReceiver()) { SVal recVal = state->getSVal(receiver, LCtx); if (recVal.isUndef()) { if (ExplodedNode *N = C.generateSink()) { BugType *BT = 0; if (msg.isPureMessageExpr()) { if (!BT_msg_undef) BT_msg_undef.reset(new BuiltinBug("Receiver in message expression " "is an uninitialized value")); BT = BT_msg_undef.get(); } else { if (!BT_objc_prop_undef) BT_objc_prop_undef.reset(new BuiltinBug("Property access on an " "uninitialized object pointer")); BT = BT_objc_prop_undef.get(); } BugReport *R = new BugReport(*BT, BT->getName(), N); R->addRange(receiver->getSourceRange()); R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, receiver, R)); C.EmitReport(R); } return; } else { // Bifurcate the state into nil and non-nil ones. DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal); ProgramStateRef notNilState, nilState; llvm::tie(notNilState, nilState) = state->assume(receiverVal); // Handle receiver must be nil. if (nilState && !notNilState) { HandleNilReceiver(C, state, msg); return; } } } const char *bugDesc = msg.isPropertySetter() ? "Argument for property setter is an uninitialized value" : "Argument in message expression is an uninitialized value"; // Check for any arguments that are uninitialized/undefined. PreVisitProcessArgs(C, CallOrObjCMessage(msg, state, LCtx), bugDesc, BT_msg_arg); }
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("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("Called C++ object pointer " "is null")); emitBadCall(BT_cxx_call_null.get(), C, CC->getCXXThisExpr()); return; } State = StNonNull; } // 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 Decl *D = Call.getDecl(); 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 ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, CheckerContext &C) const { const Expr *Ex = S->getSynchExpr(); ProgramStateRef state = C.getState(); SVal V = state->getSVal(Ex, C.getLocationContext()); // Uninitialized value used for the mutex? if (V.getAs<UndefinedVal>()) { if (ExplodedNode *N = C.generateSink()) { if (!BT_undef) BT_undef.reset(new BuiltinBug(this, "Uninitialized value used as mutex " "for @synchronized")); BugReport *report = new BugReport(*BT_undef, BT_undef->getDescription(), N); bugreporter::trackNullOrUndefValue(N, Ex, *report); C.emitReport(report); } return; } if (V.isUnknown()) return; // Check for null mutexes. ProgramStateRef notNullState, nullState; llvm::tie(notNullState, nullState) = state->assume(V.castAs<DefinedSVal>()); 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.addTransition(nullState)) { if (!BT_null) BT_null.reset(new BuiltinBug( this, "Nil value used as mutex for @synchronized() " "(no synchronization will occur)")); BugReport *report = new BugReport(*BT_null, BT_null->getDescription(), N); bugreporter::trackNullOrUndefValue(N, Ex, *report); 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); }
void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const { // If we're binding to a reference, check if the value is known to be null. if (V.isUndef()) return; const MemRegion *MR = L.getAsRegion(); const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR); if (!TVR) return; if (!TVR->getValueType()->isReferenceType()) return; ProgramStateRef State = C.getState(); ProgramStateRef StNonNull, StNull; llvm::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>()); if (StNull) { if (!StNonNull) { reportBug(StNull, S, C, /*isBind=*/true); return; } // At this point the value could be either null or non-null. // Record this as an "implicit" null dereference. if (ExplodedNode *N = C.generateSink(StNull)) { ImplicitNullDerefEvent event = { V, /*isLoad=*/true, N, &C.getBugReporter() }; dispatchEvent(event); } } // Unlike a regular null dereference, initializing a reference with a // dereferenced null pointer does not actually cause a runtime exception in // Clang's implementation of references. // // int &r = *p; // safe?? // if (p != NULL) return; // uh-oh // r = 5; // trap here // // The standard says this is invalid as soon as we try to create a "null // reference" (there is no such thing), but turning this into an assumption // that 'p' is never null will not match our actual runtime behavior. // So we do not record this assumption, allowing us to warn on the last line // of this example. // // We do need to add a transition because we may have generated a sink for // the "implicit" null dereference. C.addTransition(State, this); }
Optional<DefinedOrUnknownSVal> IntegerOverflowChecker::checkMul(CheckerContext &C, const SVal &Lhs, const SVal &Rhs, const QualType &BinType, bool &isOverflow) const { ProgramStateRef State = C.getState(); ProgramStateRef CondNotOverflow, CondPossibleOverflow; SValBuilder &SvalBuilder = C.getSValBuilder(); SVal NullSval = SvalBuilder.makeZeroVal(BinType); QualType CondType = SvalBuilder.getConditionType(); // lhs == 0 SVal LhsNotNull = SvalBuilder.evalBinOp(State, BO_NE, Lhs, NullSval, CondType); // rhs == 0 SVal RhsNotNull = SvalBuilder.evalBinOp(State, BO_NE, Rhs, NullSval, CondType); Optional<DefinedOrUnknownSVal> CondOverflow = SvalBuilder.evalBinOp(State, BO_And, LhsNotNull, RhsNotNull, CondType) .getAs<DefinedOrUnknownSVal>(); if (!CondOverflow.hasValue()) return CondOverflow; std::tie(CondPossibleOverflow, CondNotOverflow) = State->assume(*CondOverflow); if (CondNotOverflow && CondPossibleOverflow) return CondOverflow; if (CondPossibleOverflow) { // lhs * rhs SVal ValMulti = SvalBuilder.evalBinOp(State, BO_Mul, Lhs, Rhs, BinType); // First operand(lhs) is not 0 // (lhs * rhs)/lhs SVal ValDiv = SvalBuilder.evalBinOp(State, BO_Div, ValMulti, Lhs, BinType); // (lhs * rhs)/lhs != rhs CondOverflow = SvalBuilder.evalBinOp(State, BO_NE, ValDiv, Rhs, CondType) .getAs<DefinedOrUnknownSVal>(); } isOverflow = BinType->isUnsignedIntegerOrEnumerationType() || SvalBuilder.evalBinOp(State, BO_LT, Lhs, NullSval, CondType) .isZeroConstant() == SvalBuilder.evalBinOp(State, BO_LT, Rhs, NullSval, CondType) .isZeroConstant(); return CondOverflow; }
void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, CheckerContext &C) const { // Check for dereference of an undefined value. if (l.isUndef()) { if (ExplodedNode *N = C.generateSink()) { if (!BT_undef) BT_undef.reset(new BuiltinBug("Dereference of undefined pointer value")); BugReport *report = new BugReport(*BT_undef, BT_undef->getDescription(), N); bugreporter::addTrackNullOrUndefValueVisitor(N, bugreporter::GetDerefExpr(N), report); report->disablePathPruning(); C.EmitReport(report); } return; } DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(l); // Check for null dereferences. if (!isa<Loc>(location)) return; ProgramStateRef state = C.getState(); ProgramStateRef notNullState, nullState; llvm::tie(notNullState, nullState) = state->assume(location); // The explicit NULL case. if (nullState) { if (!notNullState) { reportBug(nullState, S, C); return; } // Otherwise, we have the case where the location could either be // null or not-null. Record the error node as an "implicit" null // dereference. if (ExplodedNode *N = C.generateSink(nullState)) { ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() }; dispatchEvent(event); } } // From this point forward, we know that the location is not null. C.addTransition(notNullState); }
// When checking for error code, we need to consider the following cases: // 1) noErr / [0] // 2) someErr / [1, inf] // 3) unknown // If noError, returns true iff (1). // If !noError, returns true iff (2). bool MacOSKeychainAPIChecker::definitelyReturnedError(SymbolRef RetSym, ProgramStateRef State, SValBuilder &Builder, bool noError) const { DefinedOrUnknownSVal NoErrVal = Builder.makeIntVal(NoErr, Builder.getSymbolManager().getType(RetSym)); DefinedOrUnknownSVal NoErr = Builder.evalEQ(State, NoErrVal, nonloc::SymbolVal(RetSym)); ProgramStateRef ErrState = State->assume(NoErr, noError); if (ErrState == State) { return true; } return false; }
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; unsigned id = FD->getBuiltinID(); if (!id) return false; switch (id) { case Builtin::BI__builtin_expect: { // For __builtin_expect, just return the value of the subexpression. assert (CE->arg_begin() != CE->arg_end()); SVal X = state->getSVal(*(CE->arg_begin()), LCtx); C.addTransition(state->BindExpr(CE, LCtx, X)); return true; } 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. DefinedOrUnknownSVal Size = state->getSVal(*(CE->arg_begin()), LCtx).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; } } return false; }
void IntegerOverflowChecker::checkPostStmt(const CXXNewExpr *NewExpr, CheckerContext &C) const { if (!Filter.CheckIntegerOverflowDef) return; if (NewExpr->getOperatorNew()->getOverloadedOperator() != OO_Array_New) return; const Expr *ArrSize = NewExpr->getArraySize(); SVal ElementCount = C.getSVal(ArrSize); ProgramStateRef State = C.getState(); if (makeGlobalsMembersHeuristics(ElementCount, ArrSize, C)) { C.addTransition(addToWhiteList(ElementCount, State)); return; } QualType NewExprType = NewExpr->getAllocatedType(); uint64_t NewExprTypeSize = C.getASTContext().getTypeSizeInChars(NewExprType) .getQuantity(); SValBuilder &SvalBuilder = C.getSValBuilder(); SVal NewExprTypeSizeVal = SvalBuilder.makeIntVal(NewExprTypeSize, true); bool isOverflow; Optional<DefinedOrUnknownSVal> CondOverflow = checkMul(C, NewExprTypeSizeVal, ElementCount, ArrSize->getType(), isOverflow); if (!CondOverflow) return; ProgramStateRef StateOverflow, StateNotOverflow; std::tie(StateOverflow, StateNotOverflow) = State->assume(*CondOverflow); if (!StateOverflow || (StateNotOverflow && !State->isTainted(ElementCount))) return; std::string Msg = composeMsg(StateNotOverflow, NewExprTypeSizeVal, ElementCount, 0, ArrSize, false, isOverflow, 0, C); reportBug(Msg, C, NewExpr->getExprLoc(), false); }
void CFRetainReleaseChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { // TODO: Make this check part of CallDescription. if (!Call.isGlobalCFunction()) return; // Check if we called CFRetain/CFRelease/CFMakeCollectable/CFAutorelease. if (!(Call.isCalled(CFRetain) || Call.isCalled(CFRelease) || Call.isCalled(CFMakeCollectable) || Call.isCalled(CFAutorelease))) return; // Get the argument's value. SVal ArgVal = Call.getArgSVal(0); Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>(); if (!DefArgVal) return; // Is it null? ProgramStateRef state = C.getState(); ProgramStateRef stateNonNull, stateNull; std::tie(stateNonNull, stateNull) = state->assume(*DefArgVal); if (!stateNonNull) { ExplodedNode *N = C.generateErrorNode(stateNull); if (!N) return; SmallString<64> Str; raw_svector_ostream OS(Str); OS << "Null pointer argument in call to " << cast<FunctionDecl>(Call.getDecl())->getName(); auto report = llvm::make_unique<BugReport>(BT, OS.str(), N); report->addRange(Call.getArgSourceRange(0)); bugreporter::trackExpressionValue(N, Call.getArgExpr(0), *report); C.emitReport(std::move(report)); return; } // From here on, we know the argument is non-null. C.addTransition(stateNonNull); }
/// Assumes that the collection is non-nil. /// /// If the collection is known to be nil, returns NULL to indicate an infeasible /// path. static ProgramStateRef checkCollectionNonNil(CheckerContext &C, ProgramStateRef State, const ObjCForCollectionStmt *FCS) { if (!State) return nullptr; SVal CollectionVal = C.getSVal(FCS->getCollection()); Optional<DefinedSVal> KnownCollection = CollectionVal.getAs<DefinedSVal>(); if (!KnownCollection) return State; ProgramStateRef StNonNil, StNil; std::tie(StNonNil, StNil) = State->assume(*KnownCollection); if (StNil && !StNonNil) { // The collection is nil. This path is infeasible. return nullptr; } return StNonNil; }
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"); Autorelease = &Ctx.Idents.get("CFAutorelease"); BT.reset(new APIMisuse( this, "null passed to CF memory management function")); } // Check if we called CFRetain/CFRelease/CFMakeCollectable/CFAutorelease. const IdentifierInfo *FuncII = FD->getIdentifier(); if (!(FuncII == Retain || FuncII == Release || FuncII == MakeCollectable || FuncII == Autorelease)) 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; std::tie(stateTrue, stateFalse) = state->assume(ArgIsNull); if (stateTrue && !stateFalse) { ExplodedNode *N = C.generateErrorNode(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 if (FuncII == Autorelease) description = "Null pointer argument in call to CFAutorelease"; else llvm_unreachable("impossible case"); auto report = llvm::make_unique<BugReport>(*BT, description, N); report->addRange(Arg->getSourceRange()); bugreporter::trackNullOrUndefValue(N, Arg, *report); C.emitReport(std::move(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); }
void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { // FIXME: Much of this should eventually migrate to CXXAllocatorCall. // Also, we need to decide how allocators actually work -- they're not // really part of the CXXNewExpr because they happen BEFORE the // CXXConstructExpr subexpression. See PR12014 for some discussion. StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); unsigned blockCount = currBldrCtx->blockCount(); const LocationContext *LCtx = Pred->getLocationContext(); DefinedOrUnknownSVal symVal = svalBuilder.conjureSymbolVal(0, CNE, LCtx, CNE->getType(), blockCount); ProgramStateRef State = Pred->getState(); CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef<CXXAllocatorCall> Call = CEMgr.getCXXAllocatorCall(CNE, State, LCtx); // Invalidate placement args. // FIXME: Once we figure out how we want allocators to work, // we should be using the usual pre-/(default-)eval-/post-call checks here. State = Call->invalidateRegions(blockCount); // If we're compiling with exceptions enabled, and this allocation function // is not declared as non-throwing, failures /must/ be signalled by // exceptions, and thus the return value will never be NULL. // C++11 [basic.stc.dynamic.allocation]p3. FunctionDecl *FD = CNE->getOperatorNew(); if (FD && getContext().getLangOpts().CXXExceptions) { QualType Ty = FD->getType(); if (const FunctionProtoType *ProtoType = Ty->getAs<FunctionProtoType>()) if (!ProtoType->isNothrow(getContext())) State = State->assume(symVal, true); } if (CNE->isArray()) { // FIXME: allocating an array requires simulating the constructors. // For now, just return a symbolicated region. const MemRegion *NewReg = cast<loc::MemRegionVal>(symVal).getRegion(); QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType(); const ElementRegion *EleReg = getStoreManager().GetElementZeroRegion(NewReg, ObjTy); State = State->BindExpr(CNE, Pred->getLocationContext(), loc::MemRegionVal(EleReg)); Bldr.generateNode(CNE, Pred, State); return; } // FIXME: Once we have proper support for CXXConstructExprs inside // CXXNewExpr, we need to make sure that the constructed object is not // immediately invalidated here. (The placement call should happen before // the constructor call anyway.) if (FD && FD->isReservedGlobalPlacementOperator()) { // Non-array placement new should always return the placement location. SVal PlacementLoc = State->getSVal(CNE->getPlacementArg(0), LCtx); SVal Result = svalBuilder.evalCast(PlacementLoc, CNE->getType(), CNE->getPlacementArg(0)->getType()); State = State->BindExpr(CNE, LCtx, Result); } else { State = State->BindExpr(CNE, LCtx, symVal); } // If the type is not a record, we won't have a CXXConstructExpr as an // initializer. Copy the value over. if (const Expr *Init = CNE->getInitializer()) { if (!isa<CXXConstructExpr>(Init)) { QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType(); (void)ObjTy; assert(!ObjTy->isRecordType()); SVal Location = State->getSVal(CNE, LCtx); if (isa<Loc>(Location)) State = State->bindLoc(cast<Loc>(Location), State->getSVal(Init, LCtx)); } } Bldr.generateNode(CNE, Pred, State); }
void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { // FIXME: Much of this should eventually migrate to CXXAllocatorCall. // Also, we need to decide how allocators actually work -- they're not // really part of the CXXNewExpr because they happen BEFORE the // CXXConstructExpr subexpression. See PR12014 for some discussion. unsigned blockCount = currBldrCtx->blockCount(); const LocationContext *LCtx = Pred->getLocationContext(); DefinedOrUnknownSVal symVal = UnknownVal(); FunctionDecl *FD = CNE->getOperatorNew(); bool IsStandardGlobalOpNewFunction = false; if (FD && !isa<CXXMethodDecl>(FD) && !FD->isVariadic()) { if (FD->getNumParams() == 2) { QualType T = FD->getParamDecl(1)->getType(); if (const IdentifierInfo *II = T.getBaseTypeIdentifier()) // NoThrow placement new behaves as a standard new. IsStandardGlobalOpNewFunction = II->getName().equals("nothrow_t"); } else // Placement forms are considered non-standard. IsStandardGlobalOpNewFunction = (FD->getNumParams() == 1); } // We assume all standard global 'operator new' functions allocate memory in // heap. We realize this is an approximation that might not correctly model // a custom global allocator. if (IsStandardGlobalOpNewFunction) symVal = svalBuilder.getConjuredHeapSymbolVal(CNE, LCtx, blockCount); else symVal = svalBuilder.conjureSymbolVal(nullptr, CNE, LCtx, CNE->getType(), blockCount); ProgramStateRef State = Pred->getState(); CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef<CXXAllocatorCall> Call = CEMgr.getCXXAllocatorCall(CNE, State, LCtx); // Invalidate placement args. // FIXME: Once we figure out how we want allocators to work, // we should be using the usual pre-/(default-)eval-/post-call checks here. State = Call->invalidateRegions(blockCount); if (!State) return; // If this allocation function is not declared as non-throwing, failures // /must/ be signalled by exceptions, and thus the return value will never be // NULL. -fno-exceptions does not influence this semantics. // FIXME: GCC has a -fcheck-new option, which forces it to consider the case // where new can return NULL. If we end up supporting that option, we can // consider adding a check for it here. // C++11 [basic.stc.dynamic.allocation]p3. if (FD) { QualType Ty = FD->getType(); if (const FunctionProtoType *ProtoType = Ty->getAs<FunctionProtoType>()) if (!ProtoType->isNothrow(getContext())) State = State->assume(symVal, true); } StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); if (CNE->isArray()) { // FIXME: allocating an array requires simulating the constructors. // For now, just return a symbolicated region. const MemRegion *NewReg = symVal.castAs<loc::MemRegionVal>().getRegion(); QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType(); const ElementRegion *EleReg = getStoreManager().GetElementZeroRegion(NewReg, ObjTy); State = State->BindExpr(CNE, Pred->getLocationContext(), loc::MemRegionVal(EleReg)); Bldr.generateNode(CNE, Pred, State); return; } // FIXME: Once we have proper support for CXXConstructExprs inside // CXXNewExpr, we need to make sure that the constructed object is not // immediately invalidated here. (The placement call should happen before // the constructor call anyway.) SVal Result = symVal; if (FD && FD->isReservedGlobalPlacementOperator()) { // Non-array placement new should always return the placement location. SVal PlacementLoc = State->getSVal(CNE->getPlacementArg(0), LCtx); Result = svalBuilder.evalCast(PlacementLoc, CNE->getType(), CNE->getPlacementArg(0)->getType()); } // Bind the address of the object, then check to see if we cached out. State = State->BindExpr(CNE, LCtx, Result); ExplodedNode *NewN = Bldr.generateNode(CNE, Pred, State); if (!NewN) return; // If the type is not a record, we won't have a CXXConstructExpr as an // initializer. Copy the value over. if (const Expr *Init = CNE->getInitializer()) { if (!isa<CXXConstructExpr>(Init)) { assert(Bldr.getResults().size() == 1); Bldr.takeNodes(NewN); evalBind(Dst, CNE, NewN, Result, State->getSVal(Init, LCtx), /*FirstInit=*/IsStandardGlobalOpNewFunction); } } }
void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const { ProgramStateRef state = C.getState(); if (CE->getNumArgs() < 2) { // The frontend should issue a warning for this case, so this is a sanity // check. return; } else if (CE->getNumArgs() == 3) { const Expr *Arg = CE->getArg(2); QualType QT = Arg->getType(); if (!QT->isIntegerType()) { ReportOpenBug(C, state, "Third argument to 'open' is not an integer", Arg->getSourceRange()); return; } } else if (CE->getNumArgs() > 3) { ReportOpenBug(C, state, "Call to 'open' with more than three arguments", CE->getArg(3)->getSourceRange()); return; } // The definition of O_CREAT is platform specific. We need a better way // of querying this information from the checking environment. if (!Val_O_CREAT.hasValue()) { if (C.getASTContext().getTargetInfo().getTriple().getVendor() == llvm::Triple::Apple) Val_O_CREAT = 0x0200; else { // FIXME: We need a more general way of getting the O_CREAT value. // We could possibly grovel through the preprocessor state, but // that would require passing the Preprocessor object to the ExprEngine. // See also: MallocChecker.cpp / M_ZERO. return; } } // Now check if oflags has O_CREAT set. const Expr *oflagsEx = CE->getArg(1); const SVal V = state->getSVal(oflagsEx, C.getLocationContext()); if (!V.getAs<NonLoc>()) { // The case where 'V' can be a location can only be due to a bad header, // so in this case bail out. return; } NonLoc oflags = V.castAs<NonLoc>(); NonLoc ocreateFlag = C.getSValBuilder() .makeIntVal(Val_O_CREAT.getValue(), oflagsEx->getType()).castAs<NonLoc>(); SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And, oflags, ocreateFlag, oflagsEx->getType()); if (maskedFlagsUC.isUnknownOrUndef()) return; DefinedSVal maskedFlags = maskedFlagsUC.castAs<DefinedSVal>(); // Check if maskedFlags is non-zero. ProgramStateRef trueState, falseState; std::tie(trueState, falseState) = state->assume(maskedFlags); // Only emit an error if the value of 'maskedFlags' is properly // constrained; if (!(trueState && !falseState)) return; if (CE->getNumArgs() < 3) { ReportOpenBug(C, trueState, "Call to 'open' requires a third argument when " "the 'O_CREAT' flag is set", oflagsEx->getSourceRange()); } }
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; std::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(); const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D); if (FD) { // 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() << ")"; C.emitReport( llvm::make_unique<BugReport>(*BT_call_few_args, os.str(), N)); } } // 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())); std::unique_ptr<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) { const ParmVarDecl *ParamDecl = nullptr; if(FD && i < FD->getNumParams()) ParamDecl = FD->getParamDecl(i); if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i), Call.getArgExpr(i), /*IsFirstArgument=*/i == 0, checkUninitFields, Call, *BT, ParamDecl)) return; } // If we make it here, record our assumptions about the callee. C.addTransition(State); }
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) { const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); 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 << "'."; BugReport *Report = new BugReport(*BT, os.str(), N); Report->addVisitor(new SecKeychainBugVisitor(V)); Report->addRange(ArgExpr->getSourceRange()); Report->markInteresting(AS->Region); C.emitReport(Report); } } return; } // Is it a call to one of deallocator functions? idx = getTrackedFunctionIndex(funName, false); if (idx == InvalidIdx) return; // Check the argument to the deallocator. const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); 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(); BugReport *Report = new BugReport(*BT, "Trying to free data which has not been allocated.", N); Report->addRange(ArgExpr->getSourceRange()); if (AS) Report->markInteresting(AS->Region); C.emitReport(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(); BugReport *Report = new BugReport(*BT, "Only call free if a valid (non-NULL) buffer was returned.", N); Report->addVisitor(new SecKeychainBugVisitor(ArgSM)); Report->addRange(ArgExpr->getSourceRange()); Report->markInteresting(AS->Region); C.emitReport(Report); return; } C.addTransition(State); }