void VLASizeChecker::PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS) { if (!DS->isSingleDecl()) return; const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); if (!VD) return; const VariableArrayType *VLA = C.getASTContext().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; } // Check if the size is zero. DefinedOrUnknownSVal sizeD = cast<DefinedOrUnknownSVal>(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. C.addTransition(stateNotZero); }
void ReturnUndefChecker::PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *RS) { const Expr *RetE = RS->getRetValue(); if (!RetE) return; if (!C.getState()->getSVal(RetE).isUndef()) return; ExplodedNode *N = C.GenerateSink(); if (!N) return; if (!BT) BT = new BuiltinBug("Garbage return value", "Undefined or garbage value returned to caller"); EnhancedBugReport *report = new EnhancedBugReport(*BT, BT->getDescription(), N); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, RetE); C.EmitReport(report); }
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 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 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); } } }
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); }
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 DereferenceChecker::VisitLocation(CheckerContext &C, const Stmt *S, SVal l) { // Check for dereference of an undefined value. if (l.isUndef()) { if (ExplodedNode *N = C.GenerateSink()) { if (!BT_undef) BT_undef = new BuiltinBug("Dereference of undefined pointer value"); EnhancedBugReport *report = new EnhancedBugReport(*BT_undef, BT_undef->getDescription(), N); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, bugreporter::GetDerefExpr(N)); C.EmitReport(report); } return; } DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(l); // Check for null dereferences. if (!isa<Loc>(location)) return; const GRState *state = C.getState(); const GRState *notNullState, *nullState; llvm::tie(notNullState, nullState) = state->Assume(location); // The explicit NULL case. if (nullState) { if (!notNullState) { // Generate an error node. ExplodedNode *N = C.GenerateSink(nullState); if (!N) return; // We know that 'location' cannot be non-null. This is what // we call an "explicit" null dereference. if (!BT_null) BT_null = new BuiltinBug("Dereference of null pointer"); llvm::SmallString<100> buf; llvm::SmallVector<SourceRange, 2> Ranges; switch (S->getStmtClass()) { case Stmt::UnaryOperatorClass: { const UnaryOperator *U = cast<UnaryOperator>(S); const Expr *SU = U->getSubExpr()->IgnoreParens(); if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(SU)) { if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { llvm::raw_svector_ostream os(buf); os << "Dereference of null pointer (loaded from variable '" << VD->getName() << "')"; Ranges.push_back(DR->getSourceRange()); } } break; } case Stmt::MemberExprClass: { const MemberExpr *M = cast<MemberExpr>(S); if (M->isArrow()) if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(M->getBase()->IgnoreParenCasts())) { if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { llvm::raw_svector_ostream os(buf); os << "Field access results in a dereference of a null pointer " "(loaded from variable '" << VD->getName() << "')"; Ranges.push_back(M->getBase()->getSourceRange()); } } break; } default: break; } EnhancedBugReport *report = new EnhancedBugReport(*BT_null, buf.empty() ? BT_null->getDescription():buf.str(), N); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, bugreporter::GetDerefExpr(N)); for (llvm::SmallVectorImpl<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); I!=E; ++I) report->addRange(*I); C.EmitReport(report); return; } else { // 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)) ImplicitNullDerefNodes.push_back(N); } } // From this point forward, we know that the location is not null. C.addTransition(notNullState); }