DereferenceChecker.cpp revision 9f3b9d54ccbbf212591602f389ebde7923627490
1//== NullDerefChecker.cpp - Null dereference checker ------------*- C++ -*--==// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This defines NullDerefChecker, a builtin check in ExprEngine that performs 11// checks for null pointers at loads and stores. 12// 13//===----------------------------------------------------------------------===// 14 15#include "ClangSACheckers.h" 16#include "clang/AST/ExprObjC.h" 17#include "clang/StaticAnalyzer/Core/Checker.h" 18#include "clang/StaticAnalyzer/Core/CheckerManager.h" 19#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 20#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 21#include "llvm/ADT/SmallString.h" 22 23using namespace clang; 24using namespace ento; 25 26namespace { 27class DereferenceChecker 28 : public Checker< check::Location, 29 check::Bind, 30 EventDispatcher<ImplicitNullDerefEvent> > { 31 mutable OwningPtr<BuiltinBug> BT_null; 32 mutable OwningPtr<BuiltinBug> BT_undef; 33 34 void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C, 35 bool IsBind = false) const; 36 37public: 38 void checkLocation(SVal location, bool isLoad, const Stmt* S, 39 CheckerContext &C) const; 40 void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const; 41 42 static const MemRegion *AddDerefSource(raw_ostream &os, 43 SmallVectorImpl<SourceRange> &Ranges, 44 const Expr *Ex, const ProgramState *state, 45 const LocationContext *LCtx, 46 bool loadedFrom = false); 47}; 48} // end anonymous namespace 49 50const MemRegion * 51DereferenceChecker::AddDerefSource(raw_ostream &os, 52 SmallVectorImpl<SourceRange> &Ranges, 53 const Expr *Ex, 54 const ProgramState *state, 55 const LocationContext *LCtx, 56 bool loadedFrom) { 57 Ex = Ex->IgnoreParenLValueCasts(); 58 const MemRegion *sourceR = 0; 59 switch (Ex->getStmtClass()) { 60 default: 61 break; 62 case Stmt::DeclRefExprClass: { 63 const DeclRefExpr *DR = cast<DeclRefExpr>(Ex); 64 if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { 65 os << " (" << (loadedFrom ? "loaded from" : "from") 66 << " variable '" << VD->getName() << "')"; 67 Ranges.push_back(DR->getSourceRange()); 68 sourceR = state->getLValue(VD, LCtx).getAsRegion(); 69 } 70 break; 71 } 72 case Stmt::MemberExprClass: { 73 const MemberExpr *ME = cast<MemberExpr>(Ex); 74 os << " (" << (loadedFrom ? "loaded from" : "via") 75 << " field '" << ME->getMemberNameInfo() << "')"; 76 SourceLocation L = ME->getMemberLoc(); 77 Ranges.push_back(SourceRange(L, L)); 78 break; 79 } 80 } 81 return sourceR; 82} 83 84void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S, 85 CheckerContext &C, bool IsBind) const { 86 // Generate an error node. 87 ExplodedNode *N = C.generateSink(State); 88 if (!N) 89 return; 90 91 // We know that 'location' cannot be non-null. This is what 92 // we call an "explicit" null dereference. 93 if (!BT_null) 94 BT_null.reset(new BuiltinBug("Dereference of null pointer")); 95 96 SmallString<100> buf; 97 SmallVector<SourceRange, 2> Ranges; 98 99 // Walk through lvalue casts to get the original expression 100 // that syntactically caused the load. 101 if (const Expr *expr = dyn_cast<Expr>(S)) 102 S = expr->IgnoreParenLValueCasts(); 103 104 const MemRegion *sourceR = 0; 105 106 if (IsBind) { 107 if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(S)) { 108 if (BO->isAssignmentOp()) 109 S = BO->getRHS(); 110 } else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) { 111 assert(DS->isSingleDecl() && "We process decls one by one"); 112 if (const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl())) 113 if (const Expr *Init = VD->getAnyInitializer()) 114 S = Init; 115 } 116 } 117 118 switch (S->getStmtClass()) { 119 case Stmt::ArraySubscriptExprClass: { 120 llvm::raw_svector_ostream os(buf); 121 os << "Array access"; 122 const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S); 123 sourceR = AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), 124 State.getPtr(), N->getLocationContext()); 125 os << " results in a null pointer dereference"; 126 break; 127 } 128 case Stmt::UnaryOperatorClass: { 129 llvm::raw_svector_ostream os(buf); 130 os << "Dereference of null pointer"; 131 const UnaryOperator *U = cast<UnaryOperator>(S); 132 sourceR = AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(), 133 State.getPtr(), N->getLocationContext(), true); 134 break; 135 } 136 case Stmt::MemberExprClass: { 137 const MemberExpr *M = cast<MemberExpr>(S); 138 if (M->isArrow()) { 139 llvm::raw_svector_ostream os(buf); 140 os << "Access to field '" << M->getMemberNameInfo() 141 << "' results in a dereference of a null pointer"; 142 sourceR = AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(), 143 State.getPtr(), N->getLocationContext(), true); 144 } 145 break; 146 } 147 case Stmt::ObjCIvarRefExprClass: { 148 const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S); 149 if (const DeclRefExpr *DR = 150 dyn_cast<DeclRefExpr>(IV->getBase()->IgnoreParenCasts())) { 151 if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { 152 llvm::raw_svector_ostream os(buf); 153 os << "Instance variable access (via '" << VD->getName() 154 << "') results in a null pointer dereference"; 155 } 156 } 157 Ranges.push_back(IV->getSourceRange()); 158 break; 159 } 160 default: 161 break; 162 } 163 164 BugReport *report = 165 new BugReport(*BT_null, 166 buf.empty() ? BT_null->getDescription() : buf.str(), 167 N); 168 169 report->addVisitor( 170 bugreporter::getTrackNullOrUndefValueVisitor(N, 171 bugreporter::GetDerefExpr(N), 172 report)); 173 174 for (SmallVectorImpl<SourceRange>::iterator 175 I = Ranges.begin(), E = Ranges.end(); I!=E; ++I) 176 report->addRange(*I); 177 178 if (sourceR) { 179 report->markInteresting(sourceR); 180 report->markInteresting(State->getRawSVal(loc::MemRegionVal(sourceR))); 181 } 182 183 C.EmitReport(report); 184} 185 186void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, 187 CheckerContext &C) const { 188 // Check for dereference of an undefined value. 189 if (l.isUndef()) { 190 if (ExplodedNode *N = C.generateSink()) { 191 if (!BT_undef) 192 BT_undef.reset(new BuiltinBug("Dereference of undefined pointer value")); 193 194 BugReport *report = 195 new BugReport(*BT_undef, BT_undef->getDescription(), N); 196 report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, 197 bugreporter::GetDerefExpr(N), report)); 198 report->disablePathPruning(); 199 C.EmitReport(report); 200 } 201 return; 202 } 203 204 DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(l); 205 206 // Check for null dereferences. 207 if (!isa<Loc>(location)) 208 return; 209 210 ProgramStateRef state = C.getState(); 211 212 ProgramStateRef notNullState, nullState; 213 llvm::tie(notNullState, nullState) = state->assume(location); 214 215 // The explicit NULL case. 216 if (nullState) { 217 if (!notNullState) { 218 reportBug(nullState, S, C); 219 return; 220 } 221 222 // Otherwise, we have the case where the location could either be 223 // null or not-null. Record the error node as an "implicit" null 224 // dereference. 225 if (ExplodedNode *N = C.generateSink(nullState)) { 226 ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() }; 227 dispatchEvent(event); 228 } 229 } 230 231 // From this point forward, we know that the location is not null. 232 C.addTransition(notNullState); 233} 234 235void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S, 236 CheckerContext &C) const { 237 // If we're binding to a reference, check if the value is potentially null. 238 if (V.isUndef()) 239 return; 240 241 const MemRegion *MR = L.getAsRegion(); 242 const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR); 243 if (!TVR) 244 return; 245 246 if (!TVR->getValueType()->isReferenceType()) 247 return; 248 249 ProgramStateRef State = C.getState(); 250 251 ProgramStateRef StNonNull, StNull; 252 llvm::tie(StNonNull, StNull) = State->assume(cast<DefinedOrUnknownSVal>(V)); 253 254 if (StNull) { 255 if (!StNonNull) { 256 reportBug(StNull, S, C, /*isBind=*/true); 257 return; 258 } 259 260 // At this point the value could be either null or non-null. 261 // Record this as an "implicit" null dereference. 262 if (ExplodedNode *N = C.generateSink(StNull)) { 263 ImplicitNullDerefEvent event = { V, /*isLoad=*/true, N, 264 &C.getBugReporter() }; 265 dispatchEvent(event); 266 } 267 } 268 269 // From here on out, assume the value is non-null. 270 C.addTransition(StNonNull); 271} 272 273void ento::registerDereferenceChecker(CheckerManager &mgr) { 274 mgr.registerChecker<DereferenceChecker>(); 275} 276