DereferenceChecker.cpp revision 685379965c1b105ce89cf4f6c60810932b7f4d0d
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 bugreporter::addTrackNullOrUndefValueVisitor(N, bugreporter::GetDerefExpr(N), 170 report); 171 172 for (SmallVectorImpl<SourceRange>::iterator 173 I = Ranges.begin(), E = Ranges.end(); I!=E; ++I) 174 report->addRange(*I); 175 176 if (sourceR) { 177 report->markInteresting(sourceR); 178 report->markInteresting(State->getRawSVal(loc::MemRegionVal(sourceR))); 179 } 180 181 C.EmitReport(report); 182} 183 184void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, 185 CheckerContext &C) const { 186 // Check for dereference of an undefined value. 187 if (l.isUndef()) { 188 if (ExplodedNode *N = C.generateSink()) { 189 if (!BT_undef) 190 BT_undef.reset(new BuiltinBug("Dereference of undefined pointer value")); 191 192 BugReport *report = 193 new BugReport(*BT_undef, BT_undef->getDescription(), N); 194 bugreporter::addTrackNullOrUndefValueVisitor(N, 195 bugreporter::GetDerefExpr(N), 196 report); 197 report->disablePathPruning(); 198 C.EmitReport(report); 199 } 200 return; 201 } 202 203 DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(l); 204 205 // Check for null dereferences. 206 if (!isa<Loc>(location)) 207 return; 208 209 ProgramStateRef state = C.getState(); 210 211 ProgramStateRef notNullState, nullState; 212 llvm::tie(notNullState, nullState) = state->assume(location); 213 214 // The explicit NULL case. 215 if (nullState) { 216 if (!notNullState) { 217 reportBug(nullState, S, C); 218 return; 219 } 220 221 // Otherwise, we have the case where the location could either be 222 // null or not-null. Record the error node as an "implicit" null 223 // dereference. 224 if (ExplodedNode *N = C.generateSink(nullState)) { 225 ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() }; 226 dispatchEvent(event); 227 } 228 } 229 230 // From this point forward, we know that the location is not null. 231 C.addTransition(notNullState); 232} 233 234void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S, 235 CheckerContext &C) const { 236 // If we're binding to a reference, check if the value is potentially null. 237 if (V.isUndef()) 238 return; 239 240 const MemRegion *MR = L.getAsRegion(); 241 const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR); 242 if (!TVR) 243 return; 244 245 if (!TVR->getValueType()->isReferenceType()) 246 return; 247 248 ProgramStateRef State = C.getState(); 249 250 ProgramStateRef StNonNull, StNull; 251 llvm::tie(StNonNull, StNull) = State->assume(cast<DefinedOrUnknownSVal>(V)); 252 253 if (StNull) { 254 if (!StNonNull) { 255 reportBug(StNull, S, C, /*isBind=*/true); 256 return; 257 } 258 259 // At this point the value could be either null or non-null. 260 // Record this as an "implicit" null dereference. 261 if (ExplodedNode *N = C.generateSink(StNull)) { 262 ImplicitNullDerefEvent event = { V, /*isLoad=*/true, N, 263 &C.getBugReporter() }; 264 dispatchEvent(event); 265 } 266 } 267 268 // From here on out, assume the value is non-null. 269 C.addTransition(StNonNull); 270} 271 272void ento::registerDereferenceChecker(CheckerManager &mgr) { 273 mgr.registerChecker<DereferenceChecker>(); 274} 275