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