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