DereferenceChecker.cpp revision ec8605f1d7ec846dbf51047bfd5c56d32d1ff91c
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, CheckerContext &C) const; 33 34 static void AddDerefSource(llvm::raw_ostream &os, 35 llvm::SmallVectorImpl<SourceRange> &Ranges, 36 const Expr *Ex, bool loadedFrom = false); 37}; 38} // end anonymous namespace 39 40void DereferenceChecker::AddDerefSource(llvm::raw_ostream &os, 41 llvm::SmallVectorImpl<SourceRange> &Ranges, 42 const Expr *Ex, 43 bool loadedFrom) { 44 Ex = Ex->IgnoreParenLValueCasts(); 45 switch (Ex->getStmtClass()) { 46 default: 47 return; 48 case Stmt::DeclRefExprClass: { 49 const DeclRefExpr *DR = cast<DeclRefExpr>(Ex); 50 if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { 51 os << " (" << (loadedFrom ? "loaded from" : "from") 52 << " variable '" << VD->getName() << "')"; 53 Ranges.push_back(DR->getSourceRange()); 54 } 55 return; 56 } 57 case Stmt::MemberExprClass: { 58 const MemberExpr *ME = cast<MemberExpr>(Ex); 59 os << " (" << (loadedFrom ? "loaded from" : "via") 60 << " field '" << ME->getMemberNameInfo() << "')"; 61 SourceLocation L = ME->getMemberLoc(); 62 Ranges.push_back(SourceRange(L, L)); 63 break; 64 } 65 } 66} 67 68void DereferenceChecker::checkLocation(SVal l, bool isLoad, 69 CheckerContext &C) const { 70 // Check for dereference of an undefined value. 71 if (l.isUndef()) { 72 if (ExplodedNode *N = C.generateSink()) { 73 if (!BT_undef) 74 BT_undef.reset(new BuiltinBug("Dereference of undefined pointer value")); 75 76 EnhancedBugReport *report = 77 new EnhancedBugReport(*BT_undef, BT_undef->getDescription(), N); 78 report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, 79 bugreporter::GetDerefExpr(N)); 80 C.EmitReport(report); 81 } 82 return; 83 } 84 85 DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(l); 86 87 // Check for null dereferences. 88 if (!isa<Loc>(location)) 89 return; 90 91 const Stmt *S = C.getStmt(); 92 const GRState *state = C.getState(); 93 const GRState *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 llvm::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 EnhancedBugReport *report = 161 new EnhancedBugReport(*BT_null, 162 buf.empty() ? BT_null->getDescription():buf.str(), 163 N); 164 165 report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, 166 bugreporter::GetDerefExpr(N)); 167 168 for (llvm::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