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