DereferenceChecker.cpp revision 234353
1218887Sdim//== NullDerefChecker.cpp - Null dereference checker ------------*- C++ -*--==// 2218887Sdim// 3218887Sdim// The LLVM Compiler Infrastructure 4218887Sdim// 5218887Sdim// This file is distributed under the University of Illinois Open Source 6218887Sdim// License. See LICENSE.TXT for details. 7218887Sdim// 8218887Sdim//===----------------------------------------------------------------------===// 9218887Sdim// 10218887Sdim// This defines NullDerefChecker, a builtin check in ExprEngine that performs 11218887Sdim// checks for null pointers at loads and stores. 12218887Sdim// 13218887Sdim//===----------------------------------------------------------------------===// 14218887Sdim 15221345Sdim#include "ClangSACheckers.h" 16234353Sdim#include "clang/AST/ExprObjC.h" 17221345Sdim#include "clang/StaticAnalyzer/Core/Checker.h" 18221345Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h" 19221345Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 20218887Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 21234353Sdim#include "llvm/ADT/SmallString.h" 22218887Sdim 23218887Sdimusing namespace clang; 24218887Sdimusing namespace ento; 25218887Sdim 26218887Sdimnamespace { 27221345Sdimclass DereferenceChecker 28221345Sdim : public Checker< check::Location, 29221345Sdim EventDispatcher<ImplicitNullDerefEvent> > { 30234353Sdim mutable OwningPtr<BuiltinBug> BT_null; 31234353Sdim mutable OwningPtr<BuiltinBug> BT_undef; 32221345Sdim 33218887Sdimpublic: 34226633Sdim void checkLocation(SVal location, bool isLoad, const Stmt* S, 35226633Sdim CheckerContext &C) const; 36218887Sdim 37234353Sdim static const MemRegion *AddDerefSource(raw_ostream &os, 38226633Sdim SmallVectorImpl<SourceRange> &Ranges, 39234353Sdim const Expr *Ex, const ProgramState *state, 40234353Sdim const LocationContext *LCtx, 41234353Sdim bool loadedFrom = false); 42218887Sdim}; 43218887Sdim} // end anonymous namespace 44218887Sdim 45234353Sdimconst MemRegion * 46234353SdimDereferenceChecker::AddDerefSource(raw_ostream &os, 47234353Sdim SmallVectorImpl<SourceRange> &Ranges, 48234353Sdim const Expr *Ex, 49234353Sdim const ProgramState *state, 50234353Sdim const LocationContext *LCtx, 51234353Sdim bool loadedFrom) { 52218887Sdim Ex = Ex->IgnoreParenLValueCasts(); 53234353Sdim const MemRegion *sourceR = 0; 54218887Sdim switch (Ex->getStmtClass()) { 55218887Sdim default: 56234353Sdim break; 57218887Sdim case Stmt::DeclRefExprClass: { 58218887Sdim const DeclRefExpr *DR = cast<DeclRefExpr>(Ex); 59218887Sdim if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { 60218887Sdim os << " (" << (loadedFrom ? "loaded from" : "from") 61218887Sdim << " variable '" << VD->getName() << "')"; 62218887Sdim Ranges.push_back(DR->getSourceRange()); 63234353Sdim sourceR = state->getLValue(VD, LCtx).getAsRegion(); 64218887Sdim } 65234353Sdim break; 66218887Sdim } 67218887Sdim case Stmt::MemberExprClass: { 68218887Sdim const MemberExpr *ME = cast<MemberExpr>(Ex); 69218887Sdim os << " (" << (loadedFrom ? "loaded from" : "via") 70218887Sdim << " field '" << ME->getMemberNameInfo() << "')"; 71218887Sdim SourceLocation L = ME->getMemberLoc(); 72218887Sdim Ranges.push_back(SourceRange(L, L)); 73218887Sdim break; 74218887Sdim } 75218887Sdim } 76234353Sdim return sourceR; 77218887Sdim} 78218887Sdim 79226633Sdimvoid DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, 80221345Sdim CheckerContext &C) const { 81218887Sdim // Check for dereference of an undefined value. 82218887Sdim if (l.isUndef()) { 83218887Sdim if (ExplodedNode *N = C.generateSink()) { 84218887Sdim if (!BT_undef) 85221345Sdim BT_undef.reset(new BuiltinBug("Dereference of undefined pointer value")); 86218887Sdim 87226633Sdim BugReport *report = 88226633Sdim new BugReport(*BT_undef, BT_undef->getDescription(), N); 89226633Sdim report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, 90234353Sdim bugreporter::GetDerefExpr(N), report)); 91218887Sdim C.EmitReport(report); 92218887Sdim } 93218887Sdim return; 94218887Sdim } 95218887Sdim 96218887Sdim DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(l); 97218887Sdim 98218887Sdim // Check for null dereferences. 99218887Sdim if (!isa<Loc>(location)) 100218887Sdim return; 101218887Sdim 102234353Sdim ProgramStateRef state = C.getState(); 103234353Sdim const LocationContext *LCtx = C.getLocationContext(); 104234353Sdim ProgramStateRef notNullState, nullState; 105218887Sdim llvm::tie(notNullState, nullState) = state->assume(location); 106218887Sdim 107218887Sdim // The explicit NULL case. 108218887Sdim if (nullState) { 109218887Sdim if (!notNullState) { 110218887Sdim // Generate an error node. 111218887Sdim ExplodedNode *N = C.generateSink(nullState); 112218887Sdim if (!N) 113218887Sdim return; 114218887Sdim 115218887Sdim // We know that 'location' cannot be non-null. This is what 116218887Sdim // we call an "explicit" null dereference. 117218887Sdim if (!BT_null) 118221345Sdim BT_null.reset(new BuiltinBug("Dereference of null pointer")); 119218887Sdim 120234353Sdim SmallString<100> buf; 121226633Sdim SmallVector<SourceRange, 2> Ranges; 122218887Sdim 123218887Sdim // Walk through lvalue casts to get the original expression 124218887Sdim // that syntactically caused the load. 125218887Sdim if (const Expr *expr = dyn_cast<Expr>(S)) 126218887Sdim S = expr->IgnoreParenLValueCasts(); 127234353Sdim 128234353Sdim const MemRegion *sourceR = 0; 129218887Sdim 130218887Sdim switch (S->getStmtClass()) { 131218887Sdim case Stmt::ArraySubscriptExprClass: { 132218887Sdim llvm::raw_svector_ostream os(buf); 133218887Sdim os << "Array access"; 134218887Sdim const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S); 135234353Sdim sourceR = 136234353Sdim AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), 137234353Sdim state.getPtr(), LCtx); 138218887Sdim os << " results in a null pointer dereference"; 139218887Sdim break; 140218887Sdim } 141218887Sdim case Stmt::UnaryOperatorClass: { 142218887Sdim llvm::raw_svector_ostream os(buf); 143218887Sdim os << "Dereference of null pointer"; 144218887Sdim const UnaryOperator *U = cast<UnaryOperator>(S); 145234353Sdim sourceR = 146234353Sdim AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(), 147234353Sdim state.getPtr(), LCtx, true); 148218887Sdim break; 149218887Sdim } 150218887Sdim case Stmt::MemberExprClass: { 151218887Sdim const MemberExpr *M = cast<MemberExpr>(S); 152218887Sdim if (M->isArrow()) { 153218887Sdim llvm::raw_svector_ostream os(buf); 154218887Sdim os << "Access to field '" << M->getMemberNameInfo() 155218887Sdim << "' results in a dereference of a null pointer"; 156234353Sdim sourceR = 157234353Sdim AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(), 158234353Sdim state.getPtr(), LCtx, true); 159218887Sdim } 160218887Sdim break; 161218887Sdim } 162218887Sdim case Stmt::ObjCIvarRefExprClass: { 163218887Sdim const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S); 164218887Sdim if (const DeclRefExpr *DR = 165218887Sdim dyn_cast<DeclRefExpr>(IV->getBase()->IgnoreParenCasts())) { 166218887Sdim if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { 167218887Sdim llvm::raw_svector_ostream os(buf); 168218887Sdim os << "Instance variable access (via '" << VD->getName() 169218887Sdim << "') results in a null pointer dereference"; 170218887Sdim } 171218887Sdim } 172218887Sdim Ranges.push_back(IV->getSourceRange()); 173218887Sdim break; 174218887Sdim } 175218887Sdim default: 176218887Sdim break; 177218887Sdim } 178218887Sdim 179226633Sdim BugReport *report = 180226633Sdim new BugReport(*BT_null, 181218887Sdim buf.empty() ? BT_null->getDescription():buf.str(), 182218887Sdim N); 183218887Sdim 184226633Sdim report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, 185234353Sdim bugreporter::GetDerefExpr(N), report)); 186218887Sdim 187226633Sdim for (SmallVectorImpl<SourceRange>::iterator 188218887Sdim I = Ranges.begin(), E = Ranges.end(); I!=E; ++I) 189218887Sdim report->addRange(*I); 190218887Sdim 191234353Sdim if (sourceR) { 192234353Sdim report->markInteresting(sourceR); 193234353Sdim report->markInteresting(state->getRawSVal(loc::MemRegionVal(sourceR))); 194234353Sdim } 195234353Sdim 196218887Sdim C.EmitReport(report); 197218887Sdim return; 198218887Sdim } 199218887Sdim else { 200218887Sdim // Otherwise, we have the case where the location could either be 201218887Sdim // null or not-null. Record the error node as an "implicit" null 202218887Sdim // dereference. 203221345Sdim if (ExplodedNode *N = C.generateSink(nullState)) { 204221345Sdim ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() }; 205221345Sdim dispatchEvent(event); 206221345Sdim } 207218887Sdim } 208218887Sdim } 209218887Sdim 210218887Sdim // From this point forward, we know that the location is not null. 211218887Sdim C.addTransition(notNullState); 212218887Sdim} 213221345Sdim 214221345Sdimvoid ento::registerDereferenceChecker(CheckerManager &mgr) { 215221345Sdim mgr.registerChecker<DereferenceChecker>(); 216221345Sdim} 217