Optional<DefinedOrUnknownSVal> IntegerOverflowChecker::checkSub(CheckerContext &C, const SVal &Lhs, const SVal &Rhs, const QualType &BinType, bool &isOverflow) const { SVal CondOverflow; ProgramStateRef State = C.getState(); SValBuilder &SvalBuilder = C.getSValBuilder(); SVal NullSval = SvalBuilder.makeZeroVal(BinType); QualType CondType = SvalBuilder.getConditionType(); SVal ValArgSub = SvalBuilder.evalBinOp(State, BO_Sub, Lhs, Rhs, BinType); if (BinType->isSignedIntegerType()) { // When first operand is negative // lhs < 0 SVal CondLhsLtNull = SvalBuilder.evalBinOp(State, BO_LT, Lhs, NullSval, CondType); // rhs > 0 SVal CondRhsGtNull = SvalBuilder.evalBinOp(State, BO_GT, Rhs, NullSval, CondType); // rhs > 0 && lhs < 0 SVal CondLhsLtNullRhsGtNull = SvalBuilder.evalBinOp(State, BO_And, CondLhsLtNull, CondRhsGtNull, CondType); // lhs - rhs >= 0 SVal CondArgSubGeNull = SvalBuilder.evalBinOp(State, BO_GE, ValArgSub, NullSval, CondType); // rhs > 0 && lhs < 0 && lhs-rhs >= 0 SVal CondNegativeOverflow = SvalBuilder.evalBinOp(State, BO_And, CondLhsLtNullRhsGtNull, CondArgSubGeNull, CondType); // When first operand is positive // lhs > 0 SVal CondLhsGtNull = SvalBuilder.evalBinOp(State, BO_GT, Lhs, NullSval, CondType); // rhs < 0 SVal CondRhsLtNull = SvalBuilder.evalBinOp(State, BO_LT, Rhs, NullSval, CondType); // rhs < 0 && lhs > 0 SVal CondLhsGtNullRhsLtNull = SvalBuilder.evalBinOp(State, BO_And, CondLhsGtNull, CondRhsLtNull, CondType); // lhs - rhs <= 0 SVal CondArgSubLeNull = SvalBuilder.evalBinOp(State, BO_LE, ValArgSub, NullSval, CondType); // rhs < 0 && lhs > 0 && lhs - rhs <= 0 SVal CondPositiveOverflow = SvalBuilder.evalBinOp(State, BO_And, CondLhsGtNullRhsLtNull, CondArgSubLeNull, CondType); CondOverflow = SvalBuilder.evalBinOp(State, BO_Or, CondNegativeOverflow, CondPositiveOverflow, CondType); if (!CondPositiveOverflow.isZeroConstant()) isOverflow = true; } else CondOverflow = SvalBuilder.evalBinOp(State, BO_LT, Lhs, Rhs, CondType); return CondOverflow.getAs<DefinedOrUnknownSVal>(); }
/// 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 StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const { const GRState *state = C.getState(); unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); SValBuilder &svalBuilder = C.getSValBuilder(); DefinedSVal RetVal = cast<DefinedSVal>(svalBuilder.getConjuredSymbolVal(0, CE, Count)); state = state->BindExpr(CE, RetVal); ConstraintManager &CM = C.getConstraintManager(); // Bifurcate the state into two: one with a valid FILE* pointer, the other // with a NULL. const GRState *stateNotNull, *stateNull; llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal); if (SymbolRef Sym = RetVal.getAsSymbol()) { // if RetVal is not NULL, set the symbol's state to Opened. stateNotNull = stateNotNull->set<StreamState>(Sym,StreamState::getOpened(CE)); stateNull = stateNull->set<StreamState>(Sym, StreamState::getOpenFailed(CE)); C.addTransition(stateNotNull); C.addTransition(stateNull); } }
void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const { ProgramStateRef state = C.getState(); SValBuilder &svalBuilder = C.getSValBuilder(); const LocationContext *LCtx = C.getPredecessor()->getLocationContext(); DefinedSVal RetVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()) .castAs<DefinedSVal>(); state = state->BindExpr(CE, C.getLocationContext(), RetVal); ConstraintManager &CM = C.getConstraintManager(); // Bifurcate the state into two: one with a valid FILE* pointer, the other // with a NULL. ProgramStateRef stateNotNull, stateNull; std::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal); if (SymbolRef Sym = RetVal.getAsSymbol()) { // if RetVal is not NULL, set the symbol's state to Opened. stateNotNull = stateNotNull->set<StreamMap>(Sym,StreamState::getOpened(CE)); stateNull = stateNull->set<StreamMap>(Sym, StreamState::getOpenFailed(CE)); C.addTransition(stateNotNull); C.addTransition(stateNull); } }
/// 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); }
void AdjustedReturnValueChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { // Get the result type of the call. QualType expectedResultTy = CE->getType(); // Fetch the signature of the called function. const ProgramState *state = C.getState(); const LocationContext *LCtx = C.getLocationContext(); SVal V = state->getSVal(CE, LCtx); if (V.isUnknown()) return; // Casting to void? Discard the value. if (expectedResultTy->isVoidType()) { C.addTransition(state->BindExpr(CE, LCtx, UnknownVal())); return; } const MemRegion *callee = state->getSVal(CE->getCallee(), LCtx).getAsRegion(); if (!callee) return; QualType actualResultTy; if (const FunctionTextRegion *FT = dyn_cast<FunctionTextRegion>(callee)) { const FunctionDecl *FD = FT->getDecl(); actualResultTy = FD->getResultType(); } else if (const BlockDataRegion *BD = dyn_cast<BlockDataRegion>(callee)) { const BlockTextRegion *BR = BD->getCodeRegion(); const BlockPointerType *BT=BR->getLocationType()->getAs<BlockPointerType>(); const FunctionType *FT = BT->getPointeeType()->getAs<FunctionType>(); actualResultTy = FT->getResultType(); } // Can this happen? if (actualResultTy.isNull()) return; // For now, ignore references. if (actualResultTy->getAs<ReferenceType>()) return; // Are they the same? if (expectedResultTy != actualResultTy) { // FIXME: Do more checking and actual emit an error. At least performing // the cast avoids some assertion failures elsewhere. SValBuilder &svalBuilder = C.getSValBuilder(); V = svalBuilder.evalCast(V, expectedResultTy, actualResultTy); C.addTransition(state->BindExpr(CE, LCtx, V)); } }
void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const { const Expr *E = CE->getSubExpr(); ASTContext &Ctx = C.getASTContext(); QualType ToTy = Ctx.getCanonicalType(CE->getType()); const PointerType *ToPTy = dyn_cast<PointerType>(ToTy.getTypePtr()); if (!ToPTy) return; QualType ToPointeeTy = ToPTy->getPointeeType(); // Only perform the check if 'ToPointeeTy' is a complete type. if (ToPointeeTy->isIncompleteType()) return; ProgramStateRef state = C.getState(); const MemRegion *R = state->getSVal(E, C.getLocationContext()).getAsRegion(); if (!R) return; const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R); if (!SR) return; SValBuilder &svalBuilder = C.getSValBuilder(); SVal extent = SR->getExtent(svalBuilder); const llvm::APSInt *extentInt = svalBuilder.getKnownValue(state, extent); if (!extentInt) return; CharUnits regionSize = CharUnits::fromQuantity(extentInt->getSExtValue()); CharUnits typeSize = C.getASTContext().getTypeSizeInChars(ToPointeeTy); // Ignore void, and a few other un-sizeable types. if (typeSize.isZero()) return; if (regionSize % typeSize == 0) return; if (evenFlexibleArraySize(Ctx, regionSize, typeSize, ToPointeeTy)) return; if (ExplodedNode *errorNode = C.generateErrorNode()) { if (!BT) BT.reset(new BuiltinBug(this, "Cast region with wrong size.", "Cast a region whose size is not a multiple" " of the destination type size.")); auto R = llvm::make_unique<BugReport>(*BT, BT->getDescription(), errorNode); R->addRange(CE->getSourceRange()); C.emitReport(std::move(R)); } }
void MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE) { const GRState *state = C.getState(); SValBuilder &svalBuilder = C.getSValBuilder(); SVal count = state->getSVal(CE->getArg(0)); SVal elementSize = state->getSVal(CE->getArg(1)); SVal TotalSize = svalBuilder.evalBinOp(state, BO_Mul, count, elementSize, svalBuilder.getContext().getSizeType()); SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy); C.addTransition(MallocMemAux(C, CE, TotalSize, zeroVal, state)); }
static Optional<nonloc::LazyCompoundVal> getObjectVal(const CXXConstructorDecl *CtorDecl, CheckerContext &Context) { Loc ThisLoc = Context.getSValBuilder().getCXXThis(CtorDecl->getParent(), Context.getStackFrame()); // Getting the value for 'this'. SVal This = Context.getState()->getSVal(ThisLoc); // Getting the value for '*this'. SVal Object = Context.getState()->getSVal(This.castAs<Loc>()); return Object.getAs<nonloc::LazyCompoundVal>(); }
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 UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE, CheckerContext &C) const { if (!BE->getBlockDecl()->hasCaptures()) return; ProgramStateRef state = C.getState(); const BlockDataRegion *R = cast<BlockDataRegion>(state->getSVal(BE, C.getLocationContext()).getAsRegion()); BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(), E = R->referenced_vars_end(); for (; I != E; ++I) { // This VarRegion is the region associated with the block; we need // the one associated with the encompassing context. const VarRegion *VR = *I; const VarDecl *VD = VR->getDecl(); if (VD->getAttr<BlocksAttr>() || !VD->hasLocalStorage()) continue; // Get the VarRegion associated with VD in the local stack frame. const LocationContext *LC = C.getLocationContext(); VR = C.getSValBuilder().getRegionManager().getVarRegion(VD, LC); SVal VRVal = state->getSVal(VR); if (VRVal.isUndef()) if (ExplodedNode *N = C.generateSink()) { if (!BT) BT.reset(new BuiltinBug("uninitialized variable captured by block")); // Generate a bug report. SmallString<128> buf; llvm::raw_svector_ostream os(buf); os << "Variable '" << VD->getName() << "' is uninitialized when captured by block"; BugReport *R = new BugReport(*BT, os.str(), N); if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD)) R->addRange(Ex->getSourceRange()); R->addVisitor(new FindLastStoreBRVisitor(VRVal, VR)); // need location of block C.EmitReport(R); } } }
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); }
/// Constrain the passed-in state to assume two values are equal. ProgramStateRef GTestChecker::assumeValuesEqual(SVal Val1, SVal Val2, ProgramStateRef State, CheckerContext &C) { if (!Val1.getAs<DefinedOrUnknownSVal>() || !Val2.getAs<DefinedOrUnknownSVal>()) return State; auto ValuesEqual = C.getSValBuilder().evalEQ(State, Val1.castAs<DefinedOrUnknownSVal>(), Val2.castAs<DefinedOrUnknownSVal>()); if (!ValuesEqual.getAs<DefinedSVal>()) return State; State = C.getConstraintManager().assume( State, ValuesEqual.castAs<DefinedSVal>(), true); return State; }
void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const { ProgramStateRef State = C.getState(); AllocatedDataTy ASet = State->get<AllocatedData>(); if (ASet.isEmpty()) return; bool Changed = false; AllocationPairVec Errors; for (AllocatedDataTy::iterator I = ASet.begin(), E = ASet.end(); I != E; ++I) { if (SR.isLive(I->first)) continue; Changed = true; State = State->remove<AllocatedData>(I->first); // If the allocated symbol is null or if the allocation call might have // returned an error, do not report. ConstraintManager &CMgr = State->getConstraintManager(); ConditionTruthVal AllocFailed = CMgr.isNull(State, I.getKey()); if (AllocFailed.isConstrainedTrue() || definitelyReturnedError(I->second.Region, State, C.getSValBuilder())) continue; Errors.push_back(std::make_pair(I->first, &I->second)); } if (!Changed) { // Generate the new, cleaned up state. C.addTransition(State); return; } static SimpleProgramPointTag Tag("MacOSKeychainAPIChecker : DeadSymbolsLeak"); ExplodedNode *N = C.addTransition(C.getState(), C.getPredecessor(), &Tag); // Generate the error reports. for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end(); I != E; ++I) { C.emitReport(generateAllocatedDataNotReleasedReport(*I, N, C)); } // Generate the new, cleaned up state. C.addTransition(State, N); }
// TODO: Remove this after we ensure that checkDeadSymbols are always called. void MacOSKeychainAPIChecker::checkEndPath(CheckerContext &Ctx) const { const ProgramState *state = Ctx.getState(); AllocatedSetTy AS = state->get<AllocatedData>(); if (AS.isEmpty()) return; // Anything which has been allocated but not freed (nor escaped) will be // found here, so report it. bool Changed = false; AllocationPairVec Errors; for (AllocatedSetTy::iterator I = AS.begin(), E = AS.end(); I != E; ++I ) { Changed = true; state = state->remove<AllocatedData>(I->first); // If the allocated symbol is null or if error code was returned at // allocation, do not report. if (state->getSymVal(I.getKey()) || definitelyReturnedError(I->second.Region, state, Ctx.getSValBuilder())) { continue; } Errors.push_back(std::make_pair(I->first, &I->second)); } // If no change, do not generate a new state. if (!Changed) return; ExplodedNode *N = Ctx.addTransition(state); if (!N) return; // Generate the error reports. for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end(); I != E; ++I) { Ctx.EmitReport(generateAllocatedDataNotReleasedReport(*I, N)); } }
void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const { const ProgramState *State = C.getState(); AllocatedSetTy ASet = State->get<AllocatedData>(); if (ASet.isEmpty()) return; bool Changed = false; AllocationPairVec Errors; for (AllocatedSetTy::iterator I = ASet.begin(), E = ASet.end(); I != E; ++I) { if (SR.isLive(I->first)) continue; Changed = true; State = State->remove<AllocatedData>(I->first); // If the allocated symbol is null or if the allocation call might have // returned an error, do not report. if (State->getSymVal(I->first) || definitelyReturnedError(I->second.Region, State, C.getSValBuilder())) continue; Errors.push_back(std::make_pair(I->first, &I->second)); } if (!Changed) return; // Generate the new, cleaned up state. ExplodedNode *N = C.addTransition(State); if (!N) return; // Generate the error reports. for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end(); I != E; ++I) { C.EmitReport(generateAllocatedDataNotReleasedReport(*I, N)); } }
void MPIChecker::allRegionsUsedByWait( llvm::SmallVector<const MemRegion *, 2> &ReqRegions, const MemRegion *const MR, const CallEvent &CE, CheckerContext &Ctx) const { MemRegionManager *const RegionManager = MR->getMemRegionManager(); if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) { const MemRegion *SuperRegion{nullptr}; if (const ElementRegion *const ER = MR->getAs<ElementRegion>()) { SuperRegion = ER->getSuperRegion(); } // A single request is passed to MPI_Waitall. if (!SuperRegion) { ReqRegions.push_back(MR); return; } const auto &Size = Ctx.getStoreManager().getSizeInElements( Ctx.getState(), SuperRegion, CE.getArgExpr(1)->getType()->getPointeeType()); const llvm::APSInt &ArrSize = Size.getAs<nonloc::ConcreteInt>()->getValue(); for (size_t i = 0; i < ArrSize; ++i) { const NonLoc Idx = Ctx.getSValBuilder().makeArrayIndex(i); const ElementRegion *const ER = RegionManager->getElementRegion( CE.getArgExpr(1)->getType()->getPointeeType(), Idx, SuperRegion, Ctx.getASTContext()); ReqRegions.push_back(ER->getAs<MemRegion>()); } } else if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) { ReqRegions.push_back(MR); } }
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 UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const { // 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().Target.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. return; } } // Look at the 'oflags' argument for the O_CREAT flag. const GRState *state = C.getState(); if (CE->getNumArgs() < 2) { // The frontend should issue a warning for this case, so this is a sanity // check. return; } // Now check if oflags has O_CREAT set. const Expr *oflagsEx = CE->getArg(1); const SVal V = state->getSVal(oflagsEx); if (!isa<NonLoc>(V)) { // 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 = cast<NonLoc>(V); NonLoc ocreateFlag = cast<NonLoc>(C.getSValBuilder().makeIntVal(Val_O_CREAT.getValue(), oflagsEx->getType())); SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And, oflags, ocreateFlag, oflagsEx->getType()); if (maskedFlagsUC.isUnknownOrUndef()) return; DefinedSVal maskedFlags = cast<DefinedSVal>(maskedFlagsUC); // Check if maskedFlags is non-zero. const GRState *trueState, *falseState; llvm::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) { ExplodedNode *N = C.generateSink(trueState); if (!N) return; LazyInitialize(BT_open, "Improper use of 'open'"); RangedBugReport *report = new RangedBugReport(*BT_open, "Call to 'open' requires a third argument when " "the 'O_CREAT' flag is set", N); report->addRange(oflagsEx->getSourceRange()); C.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) { 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); }
void VLASizeChecker::PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS) { if (!DS->isSingleDecl()) return; const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); if (!VD) return; ASTContext &Ctx = C.getASTContext(); const VariableArrayType *VLA = Ctx.getAsVariableArrayType(VD->getType()); if (!VLA) return; // FIXME: Handle multi-dimensional VLAs. const Expr* SE = VLA->getSizeExpr(); const GRState *state = C.getState(); SVal sizeV = state->getSVal(SE); if (sizeV.isUndef()) { // Generate an error node. ExplodedNode *N = C.generateSink(); if (!N) return; if (!BT_undef) BT_undef = new BuiltinBug("Declared variable-length array (VLA) uses a " "garbage value as its size"); EnhancedBugReport *report = new EnhancedBugReport(*BT_undef, BT_undef->getName(), N); report->addRange(SE->getSourceRange()); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); C.EmitReport(report); return; } // See if the size value is known. It can't be undefined because we would have // warned about that already. if (sizeV.isUnknown()) return; // Check if the size is zero. DefinedSVal sizeD = cast<DefinedSVal>(sizeV); const GRState *stateNotZero, *stateZero; llvm::tie(stateNotZero, stateZero) = state->assume(sizeD); if (stateZero && !stateNotZero) { ExplodedNode* N = C.generateSink(stateZero); if (!BT_zero) BT_zero = new BuiltinBug("Declared variable-length array (VLA) has zero " "size"); EnhancedBugReport *report = new EnhancedBugReport(*BT_zero, BT_zero->getName(), N); report->addRange(SE->getSourceRange()); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); C.EmitReport(report); return; } // From this point on, assume that the size is not zero. state = stateNotZero; // VLASizeChecker is responsible for defining the extent of the array being // declared. We do this by multiplying the array length by the element size, // then matching that with the array region's extent symbol. // Convert the array length to size_t. SValBuilder &svalBuilder = C.getSValBuilder(); QualType SizeTy = Ctx.getSizeType(); NonLoc ArrayLength = cast<NonLoc>(svalBuilder.evalCast(sizeD, SizeTy, SE->getType())); // Get the element size. CharUnits EleSize = Ctx.getTypeSizeInChars(VLA->getElementType()); SVal EleSizeVal = svalBuilder.makeIntVal(EleSize.getQuantity(), SizeTy); // Multiply the array length by the element size. SVal ArraySizeVal = svalBuilder.evalBinOpNN(state, BO_Mul, ArrayLength, cast<NonLoc>(EleSizeVal), SizeTy); // Finally, assume that the array's extent matches the given size. const LocationContext *LC = C.getPredecessor()->getLocationContext(); DefinedOrUnknownSVal Extent = state->getRegion(VD, LC)->getExtent(svalBuilder); DefinedOrUnknownSVal ArraySize = cast<DefinedOrUnknownSVal>(ArraySizeVal); DefinedOrUnknownSVal sizeIsKnown = svalBuilder.evalEQ(state, Extent, ArraySize); state = state->assume(sizeIsKnown, true); // Assume should not fail at this point. assert(state); // Remember our assumptions! C.addTransition(state); }
bool BuiltinFunctionChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { ProgramStateRef state = C.getState(); const FunctionDecl *FD = C.getCalleeDecl(CE); const LocationContext *LCtx = C.getLocationContext(); if (!FD) return false; switch (FD->getBuiltinID()) { default: return false; case Builtin::BI__builtin_unpredictable: case Builtin::BI__builtin_expect: case Builtin::BI__builtin_assume_aligned: case Builtin::BI__builtin_addressof: { // For __builtin_unpredictable, __builtin_expect, and // __builtin_assume_aligned, just return the value of the subexpression. // __builtin_addressof is going from a reference to a pointer, but those // are represented the same way in the analyzer. assert (CE->arg_begin() != CE->arg_end()); SVal X = state->getSVal(*(CE->arg_begin()), LCtx); C.addTransition(state->BindExpr(CE, LCtx, X)); return true; } case Builtin::BI__builtin_alloca_with_align: case Builtin::BI__builtin_alloca: { // FIXME: Refactor into StoreManager itself? MemRegionManager& RM = C.getStoreManager().getRegionManager(); const AllocaRegion* R = RM.getAllocaRegion(CE, C.blockCount(), C.getLocationContext()); // Set the extent of the region in bytes. This enables us to use the // SVal of the argument directly. If we save the extent in bits, we // cannot represent values like symbol*8. 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; } case Builtin::BI__builtin_object_size: { // This must be resolvable at compile time, so we defer to the constant // evaluator for a value. SVal V = UnknownVal(); llvm::APSInt Result; if (CE->EvaluateAsInt(Result, C.getASTContext(), Expr::SE_NoSideEffects)) { // Make sure the result has the correct type. SValBuilder &SVB = C.getSValBuilder(); BasicValueFactory &BVF = SVB.getBasicValueFactory(); BVF.getAPSIntType(CE->getType()).apply(Result); V = SVB.makeIntVal(Result); } C.addTransition(state->BindExpr(CE, LCtx, V)); return true; } } }
bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange ArgRange, const Expr *ArgEx, bool IsFirstArgument, bool CheckUninitFields, const CallEvent &Call, std::unique_ptr<BugType> &BT, const ParmVarDecl *ParamDecl ) const { const char *BD = "Uninitialized argument value"; if (uninitRefOrPointer(C, V, ArgRange, ArgEx, BT, ParamDecl, BD)) return true; if (V.isUndef()) { if (ExplodedNode *N = C.generateSink()) { LazyInit_BT(BD, BT); // Generate a report for this bug. StringRef Desc = describeUninitializedArgumentInCall(Call, IsFirstArgument); auto R = llvm::make_unique<BugReport>(*BT, Desc, N); R->addRange(ArgRange); if (ArgEx) bugreporter::trackNullOrUndefValue(N, ArgEx, *R); C.emitReport(std::move(R)); } return true; } if (!CheckUninitFields) return false; if (Optional<nonloc::LazyCompoundVal> LV = V.getAs<nonloc::LazyCompoundVal>()) { class FindUninitializedField { public: SmallVector<const FieldDecl *, 10> FieldChain; private: StoreManager &StoreMgr; MemRegionManager &MrMgr; Store store; public: FindUninitializedField(StoreManager &storeMgr, MemRegionManager &mrMgr, Store s) : StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {} bool Find(const TypedValueRegion *R) { QualType T = R->getValueType(); if (const RecordType *RT = T->getAsStructureType()) { const RecordDecl *RD = RT->getDecl()->getDefinition(); assert(RD && "Referred record has no definition"); for (const auto *I : RD->fields()) { const FieldRegion *FR = MrMgr.getFieldRegion(I, R); FieldChain.push_back(I); T = I->getType(); if (T->getAsStructureType()) { if (Find(FR)) return true; } else { const SVal &V = StoreMgr.getBinding(store, loc::MemRegionVal(FR)); if (V.isUndef()) return true; } FieldChain.pop_back(); } } return false; } }; const LazyCompoundValData *D = LV->getCVData(); FindUninitializedField F(C.getState()->getStateManager().getStoreManager(), C.getSValBuilder().getRegionManager(), D->getStore()); if (F.Find(D->getRegion())) { if (ExplodedNode *N = C.generateSink()) { LazyInit_BT(BD, BT); SmallString<512> Str; llvm::raw_svector_ostream os(Str); os << "Passed-by-value struct argument contains uninitialized data"; if (F.FieldChain.size() == 1) os << " (e.g., field: '" << *F.FieldChain[0] << "')"; else { os << " (e.g., via the field chain: '"; bool first = true; for (SmallVectorImpl<const FieldDecl *>::iterator DI = F.FieldChain.begin(), DE = F.FieldChain.end(); DI!=DE;++DI){ if (first) first = false; else os << '.'; os << **DI; } os << "')"; } // Generate a report for this bug. auto R = llvm::make_unique<BugReport>(*BT, os.str(), N); R->addRange(ArgRange); // FIXME: enhance track back for uninitialized value for arbitrary // memregions C.emitReport(std::move(R)); } return true; } } return false; }
bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange argRange, const Expr *argEx, const char *BT_desc, llvm::OwningPtr<BugType> &BT) { if (V.isUndef()) { if (ExplodedNode *N = C.generateSink()) { LazyInit_BT(BT_desc, BT); // Generate a report for this bug. BugReport *R = new BugReport(*BT, BT->getName(), N); R->addRange(argRange); if (argEx) R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, argEx)); C.EmitReport(R); } return true; } if (const nonloc::LazyCompoundVal *LV = dyn_cast<nonloc::LazyCompoundVal>(&V)) { class FindUninitializedField { public: SmallVector<const FieldDecl *, 10> FieldChain; private: ASTContext &C; StoreManager &StoreMgr; MemRegionManager &MrMgr; Store store; public: FindUninitializedField(ASTContext &c, StoreManager &storeMgr, MemRegionManager &mrMgr, Store s) : C(c), StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {} bool Find(const TypedValueRegion *R) { QualType T = R->getValueType(); if (const RecordType *RT = T->getAsStructureType()) { const RecordDecl *RD = RT->getDecl()->getDefinition(); assert(RD && "Referred record has no definition"); for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); I!=E; ++I) { const FieldRegion *FR = MrMgr.getFieldRegion(*I, R); FieldChain.push_back(*I); T = (*I)->getType(); if (T->getAsStructureType()) { if (Find(FR)) return true; } else { const SVal &V = StoreMgr.getBinding(store, loc::MemRegionVal(FR)); if (V.isUndef()) return true; } FieldChain.pop_back(); } } return false; } }; const LazyCompoundValData *D = LV->getCVData(); FindUninitializedField F(C.getASTContext(), C.getState()->getStateManager().getStoreManager(), C.getSValBuilder().getRegionManager(), D->getStore()); if (F.Find(D->getRegion())) { if (ExplodedNode *N = C.generateSink()) { LazyInit_BT(BT_desc, BT); llvm::SmallString<512> Str; llvm::raw_svector_ostream os(Str); os << "Passed-by-value struct argument contains uninitialized data"; if (F.FieldChain.size() == 1) os << " (e.g., field: '" << *F.FieldChain[0] << "')"; else { os << " (e.g., via the field chain: '"; bool first = true; for (SmallVectorImpl<const FieldDecl *>::iterator DI = F.FieldChain.begin(), DE = F.FieldChain.end(); DI!=DE;++DI){ if (first) first = false; else os << '.'; os << **DI; } os << "')"; } // Generate a report for this bug. BugReport *R = new BugReport(*BT, os.str(), N); R->addRange(argRange); // FIXME: enhance track back for uninitialized value for arbitrary // memregions C.EmitReport(R); } return true; } } return false; }
void CFRetainReleaseChecker::PreVisitCallExpr(CheckerContext& C, const CallExpr* CE) { // 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 GRState* 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 = 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 GRState *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"; EnhancedBugReport *report = new EnhancedBugReport(*BT, description, N); report->addRange(Arg->getSourceRange()); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Arg); C.EmitReport(report); return; } // From here on, we know the argument is non-null. C.addTransition(stateFalse); }
void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, const ProgramState *state, ObjCMessage msg) const { ASTContext &Ctx = C.getASTContext(); // Check the return type of the message expression. A message to nil will // return different values depending on the return type and the architecture. QualType RetTy = msg.getType(Ctx); CanQualType CanRetTy = Ctx.getCanonicalType(RetTy); if (CanRetTy->isStructureOrClassType()) { // FIXME: At some point we shouldn't rely on isConsumedExpr(), but instead // have the "use of undefined value" be smarter about where the // undefined value came from. if (C.getPredecessor()->getParentMap().isConsumedExpr(msg.getOriginExpr())){ if (ExplodedNode *N = C.generateSink(state)) emitNilReceiverBug(C, msg, N); return; } // The result is not consumed by a surrounding expression. Just propagate // the current state. C.addTransition(state); return; } // Other cases: check if the return type is smaller than void*. if (CanRetTy != Ctx.VoidTy && C.getPredecessor()->getParentMap().isConsumedExpr(msg.getOriginExpr())) { // Compute: sizeof(void *) and sizeof(return type) const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy); const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy); if (voidPtrSize < returnTypeSize && !(supportsNilWithFloatRet(Ctx.getTargetInfo().getTriple()) && (Ctx.FloatTy == CanRetTy || Ctx.DoubleTy == CanRetTy || Ctx.LongDoubleTy == CanRetTy || Ctx.LongLongTy == CanRetTy || Ctx.UnsignedLongLongTy == CanRetTy))) { if (ExplodedNode *N = C.generateSink(state)) emitNilReceiverBug(C, msg, N); return; } // Handle the safe cases where the return value is 0 if the // receiver is nil. // // FIXME: For now take the conservative approach that we only // return null values if we *know* that the receiver is nil. // This is because we can have surprises like: // // ... = [[NSScreens screens] objectAtIndex:0]; // // What can happen is that [... screens] could return nil, but // it most likely isn't nil. We should assume the semantics // of this case unless we have *a lot* more knowledge. // SVal V = C.getSValBuilder().makeZeroVal(msg.getType(Ctx)); C.generateNode(state->BindExpr(msg.getOriginExpr(), V)); return; } C.addTransition(state); }
void UnixAPIChecker::CheckOpenVariant(CheckerContext &C, const CallExpr *CE, OpenVariant Variant) const { // The index of the argument taking the flags open flags (O_RDONLY, // O_WRONLY, O_CREAT, etc.), unsigned int FlagsArgIndex; const char *VariantName; switch (Variant) { case OpenVariant::Open: FlagsArgIndex = 1; VariantName = "open"; break; case OpenVariant::OpenAt: FlagsArgIndex = 2; VariantName = "openat"; break; }; // All calls should at least provide arguments up to the 'flags' parameter. unsigned int MinArgCount = FlagsArgIndex + 1; // If the flags has O_CREAT set then open/openat() require an additional // argument specifying the file mode (permission bits) for the created file. unsigned int CreateModeArgIndex = FlagsArgIndex + 1; // The create mode argument should be the last argument. unsigned int MaxArgCount = CreateModeArgIndex + 1; ProgramStateRef state = C.getState(); if (CE->getNumArgs() < MinArgCount) { // The frontend should issue a warning for this case, so this is a sanity // check. return; } else if (CE->getNumArgs() == MaxArgCount) { const Expr *Arg = CE->getArg(CreateModeArgIndex); QualType QT = Arg->getType(); if (!QT->isIntegerType()) { SmallString<256> SBuf; llvm::raw_svector_ostream OS(SBuf); OS << "The " << CreateModeArgIndex + 1 << llvm::getOrdinalSuffix(CreateModeArgIndex + 1) << " argument to '" << VariantName << "' is not an integer"; ReportOpenBug(C, state, SBuf.c_str(), Arg->getSourceRange()); return; } } else if (CE->getNumArgs() > MaxArgCount) { SmallString<256> SBuf; llvm::raw_svector_ostream OS(SBuf); OS << "Call to '" << VariantName << "' with more than " << MaxArgCount << " arguments"; ReportOpenBug(C, state, SBuf.c_str(), CE->getArg(MaxArgCount)->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(FlagsArgIndex); 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() < MaxArgCount) { SmallString<256> SBuf; llvm::raw_svector_ostream OS(SBuf); OS << "Call to '" << VariantName << "' requires a " << CreateModeArgIndex + 1 << llvm::getOrdinalSuffix(CreateModeArgIndex + 1) << " argument when the 'O_CREAT' flag is set"; ReportOpenBug(C, trueState, SBuf.c_str(), oflagsEx->getSourceRange()); } }
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); }