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