clang API Documentation
00001 //== NullDerefChecker.cpp - Null dereference checker ------------*- C++ -*--==// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // This defines NullDerefChecker, a builtin check in ExprEngine that performs 00011 // checks for null pointers at loads and stores. 00012 // 00013 //===----------------------------------------------------------------------===// 00014 00015 #include "ClangSACheckers.h" 00016 #include "clang/AST/ExprObjC.h" 00017 #include "clang/StaticAnalyzer/Core/Checker.h" 00018 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 00019 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 00020 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 00021 #include "llvm/ADT/SmallString.h" 00022 00023 using namespace clang; 00024 using namespace ento; 00025 00026 namespace { 00027 class DereferenceChecker 00028 : public Checker< check::Location, 00029 EventDispatcher<ImplicitNullDerefEvent> > { 00030 mutable OwningPtr<BuiltinBug> BT_null; 00031 mutable OwningPtr<BuiltinBug> BT_undef; 00032 00033 public: 00034 void checkLocation(SVal location, bool isLoad, const Stmt* S, 00035 CheckerContext &C) const; 00036 00037 static const MemRegion *AddDerefSource(raw_ostream &os, 00038 SmallVectorImpl<SourceRange> &Ranges, 00039 const Expr *Ex, const ProgramState *state, 00040 const LocationContext *LCtx, 00041 bool loadedFrom = false); 00042 }; 00043 } // end anonymous namespace 00044 00045 const MemRegion * 00046 DereferenceChecker::AddDerefSource(raw_ostream &os, 00047 SmallVectorImpl<SourceRange> &Ranges, 00048 const Expr *Ex, 00049 const ProgramState *state, 00050 const LocationContext *LCtx, 00051 bool loadedFrom) { 00052 Ex = Ex->IgnoreParenLValueCasts(); 00053 const MemRegion *sourceR = 0; 00054 switch (Ex->getStmtClass()) { 00055 default: 00056 break; 00057 case Stmt::DeclRefExprClass: { 00058 const DeclRefExpr *DR = cast<DeclRefExpr>(Ex); 00059 if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { 00060 os << " (" << (loadedFrom ? "loaded from" : "from") 00061 << " variable '" << VD->getName() << "')"; 00062 Ranges.push_back(DR->getSourceRange()); 00063 sourceR = state->getLValue(VD, LCtx).getAsRegion(); 00064 } 00065 break; 00066 } 00067 case Stmt::MemberExprClass: { 00068 const MemberExpr *ME = cast<MemberExpr>(Ex); 00069 os << " (" << (loadedFrom ? "loaded from" : "via") 00070 << " field '" << ME->getMemberNameInfo() << "')"; 00071 SourceLocation L = ME->getMemberLoc(); 00072 Ranges.push_back(SourceRange(L, L)); 00073 break; 00074 } 00075 } 00076 return sourceR; 00077 } 00078 00079 void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, 00080 CheckerContext &C) const { 00081 // Check for dereference of an undefined value. 00082 if (l.isUndef()) { 00083 if (ExplodedNode *N = C.generateSink()) { 00084 if (!BT_undef) 00085 BT_undef.reset(new BuiltinBug("Dereference of undefined pointer value")); 00086 00087 BugReport *report = 00088 new BugReport(*BT_undef, BT_undef->getDescription(), N); 00089 report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, 00090 bugreporter::GetDerefExpr(N), report)); 00091 C.EmitReport(report); 00092 } 00093 return; 00094 } 00095 00096 DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(l); 00097 00098 // Check for null dereferences. 00099 if (!isa<Loc>(location)) 00100 return; 00101 00102 ProgramStateRef state = C.getState(); 00103 const LocationContext *LCtx = C.getLocationContext(); 00104 ProgramStateRef notNullState, nullState; 00105 llvm::tie(notNullState, nullState) = state->assume(location); 00106 00107 // The explicit NULL case. 00108 if (nullState) { 00109 if (!notNullState) { 00110 // Generate an error node. 00111 ExplodedNode *N = C.generateSink(nullState); 00112 if (!N) 00113 return; 00114 00115 // We know that 'location' cannot be non-null. This is what 00116 // we call an "explicit" null dereference. 00117 if (!BT_null) 00118 BT_null.reset(new BuiltinBug("Dereference of null pointer")); 00119 00120 SmallString<100> buf; 00121 SmallVector<SourceRange, 2> Ranges; 00122 00123 // Walk through lvalue casts to get the original expression 00124 // that syntactically caused the load. 00125 if (const Expr *expr = dyn_cast<Expr>(S)) 00126 S = expr->IgnoreParenLValueCasts(); 00127 00128 const MemRegion *sourceR = 0; 00129 00130 switch (S->getStmtClass()) { 00131 case Stmt::ArraySubscriptExprClass: { 00132 llvm::raw_svector_ostream os(buf); 00133 os << "Array access"; 00134 const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S); 00135 sourceR = 00136 AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), 00137 state.getPtr(), LCtx); 00138 os << " results in a null pointer dereference"; 00139 break; 00140 } 00141 case Stmt::UnaryOperatorClass: { 00142 llvm::raw_svector_ostream os(buf); 00143 os << "Dereference of null pointer"; 00144 const UnaryOperator *U = cast<UnaryOperator>(S); 00145 sourceR = 00146 AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(), 00147 state.getPtr(), LCtx, true); 00148 break; 00149 } 00150 case Stmt::MemberExprClass: { 00151 const MemberExpr *M = cast<MemberExpr>(S); 00152 if (M->isArrow()) { 00153 llvm::raw_svector_ostream os(buf); 00154 os << "Access to field '" << M->getMemberNameInfo() 00155 << "' results in a dereference of a null pointer"; 00156 sourceR = 00157 AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(), 00158 state.getPtr(), LCtx, true); 00159 } 00160 break; 00161 } 00162 case Stmt::ObjCIvarRefExprClass: { 00163 const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S); 00164 if (const DeclRefExpr *DR = 00165 dyn_cast<DeclRefExpr>(IV->getBase()->IgnoreParenCasts())) { 00166 if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { 00167 llvm::raw_svector_ostream os(buf); 00168 os << "Instance variable access (via '" << VD->getName() 00169 << "') results in a null pointer dereference"; 00170 } 00171 } 00172 Ranges.push_back(IV->getSourceRange()); 00173 break; 00174 } 00175 default: 00176 break; 00177 } 00178 00179 BugReport *report = 00180 new BugReport(*BT_null, 00181 buf.empty() ? BT_null->getDescription():buf.str(), 00182 N); 00183 00184 report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, 00185 bugreporter::GetDerefExpr(N), report)); 00186 00187 for (SmallVectorImpl<SourceRange>::iterator 00188 I = Ranges.begin(), E = Ranges.end(); I!=E; ++I) 00189 report->addRange(*I); 00190 00191 if (sourceR) { 00192 report->markInteresting(sourceR); 00193 report->markInteresting(state->getRawSVal(loc::MemRegionVal(sourceR))); 00194 } 00195 00196 C.EmitReport(report); 00197 return; 00198 } 00199 else { 00200 // Otherwise, we have the case where the location could either be 00201 // null or not-null. Record the error node as an "implicit" null 00202 // dereference. 00203 if (ExplodedNode *N = C.generateSink(nullState)) { 00204 ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() }; 00205 dispatchEvent(event); 00206 } 00207 } 00208 } 00209 00210 // From this point forward, we know that the location is not null. 00211 C.addTransition(notNullState); 00212 } 00213 00214 void ento::registerDereferenceChecker(CheckerManager &mgr) { 00215 mgr.registerChecker<DereferenceChecker>(); 00216 }