void FixedAddressChecker::PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B) { // Using a fixed address is not portable because that address will probably // not be valid in all environments or platforms. if (B->getOpcode() != BinaryOperator::Assign) return; QualType T = B->getType(); if (!T->isPointerType()) return; const GRState *state = C.getState(); SVal RV = state->getSVal(B->getRHS()); if (!RV.isConstant() || RV.isZeroConstant()) return; if (ExplodedNode *N = C.GenerateNode()) { if (!BT) BT = new BuiltinBug("Use fixed address", "Using a fixed address is not portable because that " "address will probably not be valid in all " "environments or platforms."); RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); R->addRange(B->getRHS()->getSourceRange()); C.EmitReport(R); } }
// Check the expression and if it is an iterator, generate a diagnostic // if the iterator is not valid. // FIXME: this method can generate new nodes, and subsequent logic should // use those nodes. We also cannot create multiple nodes at one ProgramPoint // with the same tag. void IteratorsChecker::checkExpr(CheckerContext &C, const Expr *E) const { const ProgramState *state = C.getState(); const MemRegion *MR = getRegion(state, E, C.getLocationContext()); if (!MR) return; // Get the state associated with the iterator. const RefState *RS = state->get<IteratorState>(MR); if (!RS) return; if (RS->isInvalid()) { if (ExplodedNode *N = C.addTransition()) { if (!BT_Invalid) // FIXME: We are eluding constness here. const_cast<IteratorsChecker*>(this)->BT_Invalid = new BuiltinBug(""); std::string msg; const MemberExpr *ME = RS->getMemberExpr(); if (ME) { std::string name = ME->getMemberNameInfo().getAsString(); msg = "Attempt to use an iterator made invalid by call to '" + name + "'"; } else { msg = "Attempt to use an iterator made invalid by copying another " "container to its container"; } BugReport *R = new BugReport(*BT_Invalid, msg, N); R->addRange(getDeclRefExpr(E)->getSourceRange()); C.EmitReport(R); } } else if (RS->isUndefined()) { if (ExplodedNode *N = C.addTransition()) { if (!BT_Undefined) // FIXME: We are eluding constness here. const_cast<IteratorsChecker*>(this)->BT_Undefined = new BuiltinBug("Use of iterator that is not defined"); BugReport *R = new BugReport(*BT_Undefined, BT_Undefined->getDescription(), N); R->addRange(getDeclRefExpr(E)->getSourceRange()); C.EmitReport(R); } } }
void ReturnPointerRangeChecker::PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *RS) { const GRState *state = C.getState(); const Expr *RetE = RS->getRetValue(); if (!RetE) return; SVal V = state->getSVal(RetE); const MemRegion *R = V.getAsRegion(); if (!R) return; R = R->StripCasts(); if (!R) return; const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(R); if (!ER) return; DefinedOrUnknownSVal &Idx = cast<DefinedOrUnknownSVal>(ER->getIndex()); // FIXME: All of this out-of-bounds checking should eventually be refactored // into a common place. DefinedOrUnknownSVal NumElements = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(), ER->getValueType()); const GRState *StInBound = state->AssumeInBound(Idx, NumElements, true); const GRState *StOutBound = state->AssumeInBound(Idx, NumElements, false); if (StOutBound && !StInBound) { ExplodedNode *N = C.GenerateSink(StOutBound); if (!N) return; // FIXME: This bug correspond to CWE-466. Eventually we should have bug // types explicitly reference such exploit categories (when applicable). if (!BT) BT = new BuiltinBug("Return of pointer value outside of expected range", "Returned pointer value points outside the original object " "(potential buffer overflow)"); // FIXME: It would be nice to eventually make this diagnostic more clear, // e.g., by referencing the original declaration or by saying *why* this // reference is outside the range. // Generate a report for this bug. RangedBugReport *report = new RangedBugReport(*BT, BT->getDescription(), N); report->addRange(RetE->getSourceRange()); C.EmitReport(report); } }
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 ArrayBoundChecker::checkLocation(SVal l, bool isLoad, CheckerContext &C) const { // Check for out of bound array element access. const MemRegion *R = l.getAsRegion(); if (!R) return; const ElementRegion *ER = dyn_cast<ElementRegion>(R); if (!ER) return; // Get the index of the accessed element. DefinedOrUnknownSVal Idx = cast<DefinedOrUnknownSVal>(ER->getIndex()); // Zero index is always in bound, this also passes ElementRegions created for // pointer casts. if (Idx.isZeroConstant()) return; const GRState *state = C.getState(); // Get the size of the array. DefinedOrUnknownSVal NumElements = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(), ER->getValueType()); const GRState *StInBound = state->assumeInBound(Idx, NumElements, true); const GRState *StOutBound = state->assumeInBound(Idx, NumElements, false); if (StOutBound && !StInBound) { ExplodedNode *N = C.generateSink(StOutBound); if (!N) return; if (!BT) BT.reset(new BuiltinBug("Out-of-bound array access", "Access out-of-bound array element (buffer overflow)")); // FIXME: It would be nice to eventually make this diagnostic more clear, // e.g., by referencing the original declaration or by saying *why* this // reference is outside the range. // Generate a report for this bug. RangedBugReport *report = new RangedBugReport(*BT, BT->getDescription(), N); report->addRange(C.getStmt()->getSourceRange()); C.EmitReport(report); return; } // Array bound check succeeded. From this point forward the array bound // should always succeed. assert(StInBound); C.addTransition(StInBound); }
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::EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE) { ExplodedNode *N = C.generateSink(); if (!N) return; BugReport *R = new BugReport(*BT, BT->getName(), N); R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, bugreporter::GetCalleeExpr(N))); C.EmitReport(R); }
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 == 0) return; const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R); if (SR == 0) 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) { if (ExplodedNode *errorNode = C.generateSink()) { if (!BT) BT.reset(new BuiltinBug("Cast region with wrong size.", "Cast a region whose size is not a multiple of the" " destination type size.")); BugReport *R = new BugReport(*BT, BT->getDescription(), errorNode); R->addRange(CE->getSourceRange()); C.EmitReport(R); } } }
void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, const Stmt *StoreE, CheckerContext &C) const { if (!val.isUndef()) return; ExplodedNode *N = C.generateSink(); if (!N) return; const char *str = "Assigned value is garbage or undefined"; if (!BT) BT.reset(new BuiltinBug(str)); // Generate a report for this bug. const Expr *ex = 0; while (StoreE) { if (const BinaryOperator *B = dyn_cast<BinaryOperator>(StoreE)) { if (B->isCompoundAssignmentOp()) { ProgramStateRef state = C.getState(); if (state->getSVal(B->getLHS(), C.getLocationContext()).isUndef()) { str = "The left expression of the compound assignment is an " "uninitialized value. The computed value will also be garbage"; ex = B->getLHS(); break; } } ex = B->getRHS(); break; } if (const DeclStmt *DS = dyn_cast<DeclStmt>(StoreE)) { const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); ex = VD->getInit(); } break; } BugReport *R = new BugReport(*BT, str, N); if (ex) { R->addRange(ex->getSourceRange()); bugreporter::trackNullOrUndefValue(N, ex, *R); } C.EmitReport(R); }
// Handle operator calls. First, if it is operator=, check the argument, // and handle assigning and set target state appropriately. Otherwise, for // other operators, check the args for bad iterators and handle comparisons. void IteratorsChecker::checkPreStmt(const CXXOperatorCallExpr *OCE, CheckerContext &C) const { const LocationContext *LC = C.getLocationContext(); const ProgramState *state = C.getState(); OverloadedOperatorKind Kind = OCE->getOperator(); if (Kind == OO_Equal) { checkExpr(C, OCE->getArg(1)); state = handleAssign(state, OCE->getArg(0), OCE->getArg(1), LC); C.addTransition(state); return; } else { checkArgs(C, OCE); // If it is a compare and both are iterators, ensure that they are for // the same container. if (Kind == OO_EqualEqual || Kind == OO_ExclaimEqual || Kind == OO_Less || Kind == OO_LessEqual || Kind == OO_Greater || Kind == OO_GreaterEqual) { const MemRegion *MR0, *MR1; MR0 = getRegion(state, OCE->getArg(0), LC); if (!MR0) return; MR1 = getRegion(state, OCE->getArg(1), LC); if (!MR1) return; const RefState *RS0, *RS1; RS0 = state->get<IteratorState>(MR0); if (!RS0) return; RS1 = state->get<IteratorState>(MR1); if (!RS1) return; if (RS0->getMemRegion() != RS1->getMemRegion()) { if (ExplodedNode *N = C.addTransition()) { if (!BT_Incompatible) const_cast<IteratorsChecker*>(this)->BT_Incompatible = new BuiltinBug( "Cannot compare iterators from different containers"); BugReport *R = new BugReport(*BT_Incompatible, BT_Incompatible->getDescription(), N); R->addRange(OCE->getSourceRange()); C.EmitReport(R); } } } } }
void UndefResultChecker::checkPostStmt(const BinaryOperator *B, CheckerContext &C) const { const ProgramState *state = C.getState(); const LocationContext *LCtx = C.getLocationContext(); if (state->getSVal(B, LCtx).isUndef()) { // Generate an error node. ExplodedNode *N = C.generateSink(); if (!N) return; if (!BT) BT.reset(new BuiltinBug("Result of operation is garbage or undefined")); llvm::SmallString<256> sbuf; llvm::raw_svector_ostream OS(sbuf); const Expr *Ex = NULL; bool isLeft = true; if (state->getSVal(B->getLHS(), LCtx).isUndef()) { Ex = B->getLHS()->IgnoreParenCasts(); isLeft = true; } else if (state->getSVal(B->getRHS(), LCtx).isUndef()) { Ex = B->getRHS()->IgnoreParenCasts(); isLeft = false; } if (Ex) { OS << "The " << (isLeft ? "left" : "right") << " operand of '" << BinaryOperator::getOpcodeStr(B->getOpcode()) << "' is a garbage value"; } else { // Neither operand was undefined, but the result is undefined. OS << "The result of the '" << BinaryOperator::getOpcodeStr(B->getOpcode()) << "' expression is undefined"; } BugReport *report = new BugReport(*BT, OS.str(), N); if (Ex) { report->addRange(Ex->getSourceRange()); report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex)); } else report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, B)); C.EmitReport(report); } }
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); }
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); } } }
void ExprInspectionChecker::analyzerEval(const CallExpr *CE, CheckerContext &C) const { ExplodedNode *N = C.getPredecessor(); const LocationContext *LC = N->getLocationContext(); // A specific instantiation of an inlined function may have more constrained // values than can generally be assumed. Skip the check. if (LC->getCurrentStackFrame()->getParent() != 0) return; if (!BT) BT.reset(new BugType("Checking analyzer assumptions", "debug")); BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N); C.EmitReport(R); }
void DivZeroChecker::reportBug(const char *Msg, ProgramStateRef StateZero, CheckerContext &C) const { if (ExplodedNode *N = C.generateSink(StateZero)) { if (!BT) BT.reset(new BuiltinBug("Division by zero")); BugReport *R = new BugReport(*BT, Msg, N); bugreporter::addTrackNullOrUndefValueVisitor(N, bugreporter::GetDenomExpr(N), R); C.EmitReport(R); } }
void CastSizeChecker::PreVisitCastExpr(CheckerContext &C, const CastExpr *CE) { const Expr *E = CE->getSubExpr(); ASTContext &Ctx = C.getASTContext(); QualType ToTy = Ctx.getCanonicalType(CE->getType()); PointerType *ToPTy = dyn_cast<PointerType>(ToTy.getTypePtr()); if (!ToPTy) return; QualType ToPointeeTy = ToPTy->getPointeeType(); const GRState *state = C.getState(); const MemRegion *R = state->getSVal(E).getAsRegion(); if (R == 0) return; const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R); if (SR == 0) return; ValueManager &ValMgr = C.getValueManager(); SVal Extent = SR->getExtent(ValMgr); SValuator &SVator = ValMgr.getSValuator(); const llvm::APSInt *ExtentInt = SVator.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) { if (ExplodedNode *N = C.GenerateSink()) { if (!BT) BT = new BuiltinBug("Cast region with wrong size.", "Cast a region whose size is not a multiple of the" " destination type size."); RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); R->addRange(CE->getSourceRange()); C.EmitReport(R); } } }
void DivZeroChecker::PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B) { BinaryOperator::Opcode Op = B->getOpcode(); if (Op != BO_Div && Op != BO_Rem && Op != BO_DivAssign && Op != BO_RemAssign) return; if (!B->getRHS()->getType()->isIntegerType() || !B->getRHS()->getType()->isScalarType()) return; SVal Denom = C.getState()->getSVal(B->getRHS()); const DefinedSVal *DV = dyn_cast<DefinedSVal>(&Denom); // Divide-by-undefined handled in the generic checking for uses of // undefined values. if (!DV) return; // Check for divide by zero. ConstraintManager &CM = C.getConstraintManager(); const GRState *stateNotZero, *stateZero; llvm::tie(stateNotZero, stateZero) = CM.assumeDual(C.getState(), *DV); if (stateZero && !stateNotZero) { if (ExplodedNode *N = C.generateSink(stateZero)) { if (!BT) BT = new BuiltinBug("Division by zero"); EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getDescription(), N); R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, bugreporter::GetDenomExpr(N)); C.EmitReport(R); } return; } // If we get here, then the denom should not be zero. We abandon the implicit // zero denom case for now. C.addTransition(stateNotZero); }
void UndefinedArraySubscriptChecker::PreVisitArraySubscriptExpr(CheckerContext &C, const ArraySubscriptExpr *A) { if (C.getState()->getSVal(A->getIdx()).isUndef()) { if (ExplodedNode *N = C.GenerateSink()) { if (!BT) BT = new BuiltinBug("Array subscript is undefined"); // Generate a report for this bug. EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); R->addRange(A->getIdx()->getSourceRange()); R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, A->getIdx()); C.EmitReport(R); } } }
// Check if the location is a freed symbolic region. void MallocChecker::checkLocation(SVal l, bool isLoad,CheckerContext &C) const { SymbolRef Sym = l.getLocSymbolInBase(); if (Sym) { const RefState *RS = C.getState()->get<RegionState>(Sym); if (RS && RS->isReleased()) { if (ExplodedNode *N = C.generateNode()) { if (!BT_UseFree) BT_UseFree.reset(new BuiltinBug("Use dynamically allocated memory " "after it is freed.")); BugReport *R = new BugReport(*BT_UseFree, BT_UseFree->getDescription(), N); C.EmitReport(R); } } } }
void NilArgChecker::WarnNilArg(CheckerContext &C, const ObjCMessage &msg, unsigned int Arg) { if (!BT) BT = new APIMisuse("nil argument"); if (ExplodedNode *N = C.generateSink()) { llvm::SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); os << "Argument to '" << GetReceiverNameType(msg) << "' method '" << msg.getSelector().getAsString() << "' cannot be nil"; RangedBugReport *R = new RangedBugReport(*BT, os.str(), N); R->addRange(msg.getArgSourceRange(Arg)); C.EmitReport(R); } }
void ObjCContainersChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { StringRef Name = C.getCalleeName(CE); if (Name.empty() || CE->getNumArgs() < 2) return; // Check the array access. if (Name.equals("CFArrayGetValueAtIndex")) { ProgramStateRef State = C.getState(); // Retrieve the size. // Find out if we saw this array symbol before and have information about it. const Expr *ArrayExpr = CE->getArg(0); SymbolRef ArraySym = getArraySym(ArrayExpr, C); if (!ArraySym) return; const DefinedSVal *Size = State->get<ArraySizeMap>(ArraySym); if (!Size) return; // Get the index. const Expr *IdxExpr = CE->getArg(1); SVal IdxVal = State->getSVal(IdxExpr, C.getLocationContext()); if (IdxVal.isUnknownOrUndef()) return; DefinedSVal Idx = cast<DefinedSVal>(IdxVal); // Now, check if 'Idx in [0, Size-1]'. const QualType T = IdxExpr->getType(); ProgramStateRef StInBound = State->assumeInBound(Idx, *Size, true, T); ProgramStateRef StOutBound = State->assumeInBound(Idx, *Size, false, T); if (StOutBound && !StInBound) { ExplodedNode *N = C.generateSink(StOutBound); if (!N) return; initBugType(); BugReport *R = new BugReport(*BT, "Index is out of bounds", N); R->addRange(IdxExpr->getSourceRange()); C.EmitReport(R); return; } } }
void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const { ExplodedNode *N = C.getPredecessor(); const LocationContext *LC = N->getLocationContext(); // An inlined function could conceivably also be analyzed as a top-level // function. We ignore this case and only emit a message (TRUE or FALSE) // when we are analyzing it as an inlined function. This means that // clang_analyzer_checkInlined(true) should always print TRUE, but // clang_analyzer_checkInlined(false) should never actually print anything. if (LC->getCurrentStackFrame()->getParent() == 0) return; if (!BT) BT.reset(new BugType("Checking analyzer assumptions", "debug")); BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N); C.EmitReport(R); }
// FIXME: Eventually this should be rolled into the MallocChecker, but this // check is more basic and is valuable for widespread use. void UnixAPIChecker::CheckMallocZero(CheckerContext &C, const CallExpr *CE) const { // Sanity check that malloc takes one argument. if (CE->getNumArgs() != 1) return; // Check if the allocation size is 0. const GRState *state = C.getState(); SVal argVal = state->getSVal(CE->getArg(0)); if (argVal.isUnknownOrUndef()) return; const GRState *trueState, *falseState; llvm::tie(trueState, falseState) = state->assume(cast<DefinedSVal>(argVal)); // Is the value perfectly constrained to zero? if (falseState && !trueState) { ExplodedNode *N = C.generateSink(falseState); if (!N) return; // FIXME: Add reference to CERT advisory, and/or C99 standard in bug // output. LazyInitialize(BT_mallocZero, "Undefined allocation of 0 bytes"); EnhancedBugReport *report = new EnhancedBugReport(*BT_mallocZero, "Call to 'malloc' has an allocation" " size of 0 bytes", N); report->addRange(CE->getArg(0)->getSourceRange()); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, CE->getArg(0)); C.EmitReport(report); return; } // Assume the the value is non-zero going forward. assert(trueState); if (trueState != state) { C.addTransition(trueState); } }
static void checkForInvalidSelf(const Expr *E, CheckerContext &C, const char *errorStr) { if (!E) return; if (!C.getState()->get<CalledInit>()) return; if (!isInvalidSelf(E, C)) return; // Generate an error node. ExplodedNode *N = C.generateSink(); if (!N) return; BugReport *report = new BugReport(*new InitSelfBug(), errorStr, N); C.EmitReport(report); }
void StreamChecker::checkEndPath(CheckerContext &Ctx) const { const ProgramState *state = Ctx.getState(); typedef llvm::ImmutableMap<SymbolRef, StreamState> SymMap; SymMap M = state->get<StreamState>(); for (SymMap::iterator I = M.begin(), E = M.end(); I != E; ++I) { StreamState SS = I->second; if (SS.isOpened()) { ExplodedNode *N = Ctx.addTransition(state); if (N) { if (!BT_ResourceLeak) BT_ResourceLeak.reset(new BuiltinBug("Resource Leak", "Opened File never closed. Potential Resource leak.")); BugReport *R = new BugReport(*BT_ResourceLeak, BT_ResourceLeak->getDescription(), N); Ctx.EmitReport(R); } } } }
static void CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, BugType *&BT, const IdentifierInfo *FI) { if (!BT) { llvm::SmallString<128> S; llvm::raw_svector_ostream os(S); os << "Improper use of '" << FI->getName() << '\''; BT = new BugType(os.str(), "Mac OS X API"); } if (CE->getNumArgs() < 1) return; // Check if the first argument is stack allocated. If so, issue a warning // because that's likely to be bad news. const GRState *state = C.getState(); const MemRegion *R = state->getSVal(CE->getArg(0)).getAsRegion(); if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) return; ExplodedNode *N = C.generateSink(state); if (!N) return; llvm::SmallString<256> S; llvm::raw_svector_ostream os(S); os << "Call to '" << FI->getName() << "' uses"; if (const VarRegion *VR = dyn_cast<VarRegion>(R)) os << " the local variable '" << VR->getDecl()->getName() << '\''; else os << " stack allocated memory"; os << " for the predicate value. Using such transient memory for " "the predicate is potentially dangerous."; if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace())) os << " Perhaps you intended to declare the variable as 'static'?"; EnhancedBugReport *report = new EnhancedBugReport(*BT, os.str(), N); report->addRange(CE->getArg(0)->getSourceRange()); C.EmitReport(report); }
void ArrayBoundCheckerV2::reportOOB(CheckerContext &checkerContext, const ProgramState *errorState, OOB_Kind kind) const { ExplodedNode *errorNode = checkerContext.generateSink(errorState); if (!errorNode) return; if (!BT) BT.reset(new BuiltinBug("Out-of-bound access")); // FIXME: This diagnostics are preliminary. We should get far better // diagnostics for explaining buffer overruns. llvm::SmallString<256> buf; llvm::raw_svector_ostream os(buf); os << "Out of bound memory access " << (kind == OOB_Precedes ? "(accessed memory precedes memory block)" : "(access exceeds upper limit of memory block)"); checkerContext.EmitReport(new BugReport(*BT, os.str(), errorNode)); }
void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C, const CallExpr *CE) const { // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker. // They can possibly be refactored. if (CE->getNumArgs() < 1) return; // Check if the first argument is stack allocated. If so, issue a warning // because that's likely to be bad news. const ProgramState *state = C.getState(); const MemRegion *R = state->getSVal(CE->getArg(0), C.getLocationContext()).getAsRegion(); if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) return; ExplodedNode *N = C.generateSink(state); if (!N) return; llvm::SmallString<256> S; llvm::raw_svector_ostream os(S); os << "Call to 'pthread_once' uses"; if (const VarRegion *VR = dyn_cast<VarRegion>(R)) os << " the local variable '" << VR->getDecl()->getName() << '\''; else os << " stack allocated memory"; os << " for the \"control\" value. Using such transient memory for " "the control value is potentially dangerous."; if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace())) os << " Perhaps you intended to declare the variable as 'static'?"; LazyInitialize(BT_pthreadOnce, "Improper use of 'pthread_once'"); BugReport *report = new BugReport(*BT_pthreadOnce, os.str(), N); report->addRange(CE->getArg(0)->getSourceRange()); C.EmitReport(report); }
// Generates an error report, indicating that the function whose name is given // will perform a zero byte allocation. // Returns false if an error occured, true otherwise. bool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C, const ProgramState *falseState, const Expr *arg, const char *fn_name) const { ExplodedNode *N = C.generateSink(falseState); if (!N) return false; LazyInitialize(BT_mallocZero, "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)"); llvm::SmallString<256> S; llvm::raw_svector_ostream os(S); os << "Call to '" << fn_name << "' has an allocation size of 0 bytes"; BugReport *report = new BugReport(*BT_mallocZero, os.str(), N); report->addRange(arg->getSourceRange()); report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, arg)); C.EmitReport(report); return true; }
void PointerSubChecker::checkPreStmt(const BinaryOperator *B, CheckerContext &C) const { // When doing pointer subtraction, if the two pointers do not point to the // same memory chunk, emit a warning. if (B->getOpcode() != BO_Sub) return; ProgramStateRef state = C.getState(); const LocationContext *LCtx = C.getLocationContext(); SVal LV = state->getSVal(B->getLHS(), LCtx); SVal RV = state->getSVal(B->getRHS(), LCtx); const MemRegion *LR = LV.getAsRegion(); const MemRegion *RR = RV.getAsRegion(); if (!(LR && RR)) return; const MemRegion *BaseLR = LR->getBaseRegion(); const MemRegion *BaseRR = RR->getBaseRegion(); if (BaseLR == BaseRR) return; // Allow arithmetic on different symbolic regions. if (isa<SymbolicRegion>(BaseLR) || isa<SymbolicRegion>(BaseRR)) return; if (ExplodedNode *N = C.addTransition()) { if (!BT) BT.reset(new BuiltinBug("Pointer subtraction", "Subtraction of two pointers that do not point to " "the same memory chunk may cause incorrect result.")); BugReport *R = new BugReport(*BT, BT->getDescription(), N); R->addRange(B->getSourceRange()); C.EmitReport(R); } }