DereferenceChecker.cpp revision 76aadc346c3a4c363238a1e1232f324c3355d9e0
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 EventDispatcher<ImplicitNullDerefEvent> > { 30 mutable OwningPtr<BuiltinBug> BT_null; 31 mutable OwningPtr<BuiltinBug> BT_undef; 32 33public: 34 void checkLocation(SVal location, bool isLoad, const Stmt* S, 35 CheckerContext &C) const; 36 37 static const MemRegion *AddDerefSource(raw_ostream &os, 38 SmallVectorImpl<SourceRange> &Ranges, 39 const Expr *Ex, const ProgramState *state, 40 const LocationContext *LCtx, 41 bool loadedFrom = false); 42}; 43} // end anonymous namespace 44 45const MemRegion * 46DereferenceChecker::AddDerefSource(raw_ostream &os, 47 SmallVectorImpl<SourceRange> &Ranges, 48 const Expr *Ex, 49 const ProgramState *state, 50 const LocationContext *LCtx, 51 bool loadedFrom) { 52 Ex = Ex->IgnoreParenLValueCasts(); 53 const MemRegion *sourceR = 0; 54 switch (Ex->getStmtClass()) { 55 default: 56 break; 57 case Stmt::DeclRefExprClass: { 58 const DeclRefExpr *DR = cast<DeclRefExpr>(Ex); 59 if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { 60 os << " (" << (loadedFrom ? "loaded from" : "from") 61 << " variable '" << VD->getName() << "')"; 62 Ranges.push_back(DR->getSourceRange()); 63 sourceR = state->getLValue(VD, LCtx).getAsRegion(); 64 } 65 break; 66 } 67 case Stmt::MemberExprClass: { 68 const MemberExpr *ME = cast<MemberExpr>(Ex); 69 os << " (" << (loadedFrom ? "loaded from" : "via") 70 << " field '" << ME->getMemberNameInfo() << "')"; 71 SourceLocation L = ME->getMemberLoc(); 72 Ranges.push_back(SourceRange(L, L)); 73 break; 74 } 75 } 76 return sourceR; 77} 78 79void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, 80 CheckerContext &C) const { 81 // Check for dereference of an undefined value. 82 if (l.isUndef()) { 83 if (ExplodedNode *N = C.generateSink()) { 84 if (!BT_undef) 85 BT_undef.reset(new BuiltinBug("Dereference of undefined pointer value")); 86 87 BugReport *report = 88 new BugReport(*BT_undef, BT_undef->getDescription(), N); 89 report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, 90 bugreporter::GetDerefExpr(N), report)); 91 C.EmitReport(report); 92 } 93 return; 94 } 95 96 DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(l); 97 98 // Check for null dereferences. 99 if (!isa<Loc>(location)) 100 return; 101 102 ProgramStateRef state = C.getState(); 103 const LocationContext *LCtx = C.getLocationContext(); 104 ProgramStateRef notNullState, nullState; 105 llvm::tie(notNullState, nullState) = state->assume(location); 106 107 // The explicit NULL case. 108 if (nullState) { 109 if (!notNullState) { 110 // Generate an error node. 111 ExplodedNode *N = C.generateSink(nullState); 112 if (!N) 113 return; 114 115 // We know that 'location' cannot be non-null. This is what 116 // we call an "explicit" null dereference. 117 if (!BT_null) 118 BT_null.reset(new BuiltinBug("Dereference of null pointer")); 119 120 SmallString<100> buf; 121 SmallVector<SourceRange, 2> Ranges; 122 123 // Walk through lvalue casts to get the original expression 124 // that syntactically caused the load. 125 if (const Expr *expr = dyn_cast<Expr>(S)) 126 S = expr->IgnoreParenLValueCasts(); 127 128 const MemRegion *sourceR = 0; 129 130 switch (S->getStmtClass()) { 131 case Stmt::ArraySubscriptExprClass: { 132 llvm::raw_svector_ostream os(buf); 133 os << "Array access"; 134 const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S); 135 sourceR = 136 AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), 137 state.getPtr(), LCtx); 138 os << " results in a null pointer dereference"; 139 break; 140 } 141 case Stmt::UnaryOperatorClass: { 142 llvm::raw_svector_ostream os(buf); 143 os << "Dereference of null pointer"; 144 const UnaryOperator *U = cast<UnaryOperator>(S); 145 sourceR = 146 AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(), 147 state.getPtr(), LCtx, true); 148 break; 149 } 150 case Stmt::MemberExprClass: { 151 const MemberExpr *M = cast<MemberExpr>(S); 152 if (M->isArrow()) { 153 llvm::raw_svector_ostream os(buf); 154 os << "Access to field '" << M->getMemberNameInfo() 155 << "' results in a dereference of a null pointer"; 156 sourceR = 157 AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(), 158 state.getPtr(), LCtx, true); 159 } 160 break; 161 } 162 case Stmt::ObjCIvarRefExprClass: { 163 const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S); 164 if (const DeclRefExpr *DR = 165 dyn_cast<DeclRefExpr>(IV->getBase()->IgnoreParenCasts())) { 166 if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { 167 llvm::raw_svector_ostream os(buf); 168 os << "Instance variable access (via '" << VD->getName() 169 << "') results in a null pointer dereference"; 170 } 171 } 172 Ranges.push_back(IV->getSourceRange()); 173 break; 174 } 175 default: 176 break; 177 } 178 179 BugReport *report = 180 new BugReport(*BT_null, 181 buf.empty() ? BT_null->getDescription():buf.str(), 182 N); 183 184 report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, 185 bugreporter::GetDerefExpr(N), report)); 186 187 for (SmallVectorImpl<SourceRange>::iterator 188 I = Ranges.begin(), E = Ranges.end(); I!=E; ++I) 189 report->addRange(*I); 190 191 if (sourceR) { 192 report->markInteresting(sourceR); 193 report->markInteresting(state->getRawSVal(loc::MemRegionVal(sourceR))); 194 } 195 196 C.EmitReport(report); 197 return; 198 } 199 else { 200 // Otherwise, we have the case where the location could either be 201 // null or not-null. Record the error node as an "implicit" null 202 // dereference. 203 if (ExplodedNode *N = C.generateSink(nullState)) { 204 ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() }; 205 dispatchEvent(event); 206 } 207 } 208 } 209 210 // From this point forward, we know that the location is not null. 211 C.addTransition(notNullState); 212} 213 214void ento::registerDereferenceChecker(CheckerManager &mgr) { 215 mgr.registerChecker<DereferenceChecker>(); 216} 217