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