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/BugReporter/BugType.h" 00018 #include "clang/StaticAnalyzer/Core/Checker.h" 00019 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 00020 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 00021 #include "llvm/ADT/SmallString.h" 00022 #include "llvm/Support/raw_ostream.h" 00023 00024 using namespace clang; 00025 using namespace ento; 00026 00027 namespace { 00028 class DereferenceChecker 00029 : public Checker< check::Location, 00030 check::Bind, 00031 EventDispatcher<ImplicitNullDerefEvent> > { 00032 mutable std::unique_ptr<BuiltinBug> BT_null; 00033 mutable std::unique_ptr<BuiltinBug> BT_undef; 00034 00035 void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C, 00036 bool IsBind = false) const; 00037 00038 public: 00039 void checkLocation(SVal location, bool isLoad, const Stmt* S, 00040 CheckerContext &C) const; 00041 void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const; 00042 00043 static void AddDerefSource(raw_ostream &os, 00044 SmallVectorImpl<SourceRange> &Ranges, 00045 const Expr *Ex, const ProgramState *state, 00046 const LocationContext *LCtx, 00047 bool loadedFrom = false); 00048 }; 00049 } // end anonymous namespace 00050 00051 void 00052 DereferenceChecker::AddDerefSource(raw_ostream &os, 00053 SmallVectorImpl<SourceRange> &Ranges, 00054 const Expr *Ex, 00055 const ProgramState *state, 00056 const LocationContext *LCtx, 00057 bool loadedFrom) { 00058 Ex = Ex->IgnoreParenLValueCasts(); 00059 switch (Ex->getStmtClass()) { 00060 default: 00061 break; 00062 case Stmt::DeclRefExprClass: { 00063 const DeclRefExpr *DR = cast<DeclRefExpr>(Ex); 00064 if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { 00065 os << " (" << (loadedFrom ? "loaded from" : "from") 00066 << " variable '" << VD->getName() << "')"; 00067 Ranges.push_back(DR->getSourceRange()); 00068 } 00069 break; 00070 } 00071 case Stmt::MemberExprClass: { 00072 const MemberExpr *ME = cast<MemberExpr>(Ex); 00073 os << " (" << (loadedFrom ? "loaded from" : "via") 00074 << " field '" << ME->getMemberNameInfo() << "')"; 00075 SourceLocation L = ME->getMemberLoc(); 00076 Ranges.push_back(SourceRange(L, L)); 00077 break; 00078 } 00079 case Stmt::ObjCIvarRefExprClass: { 00080 const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex); 00081 os << " (" << (loadedFrom ? "loaded from" : "via") 00082 << " ivar '" << IV->getDecl()->getName() << "')"; 00083 SourceLocation L = IV->getLocation(); 00084 Ranges.push_back(SourceRange(L, L)); 00085 break; 00086 } 00087 } 00088 } 00089 00090 void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S, 00091 CheckerContext &C, bool IsBind) const { 00092 // Generate an error node. 00093 ExplodedNode *N = C.generateSink(State); 00094 if (!N) 00095 return; 00096 00097 // We know that 'location' cannot be non-null. This is what 00098 // we call an "explicit" null dereference. 00099 if (!BT_null) 00100 BT_null.reset(new BuiltinBug(this, "Dereference of null pointer")); 00101 00102 SmallString<100> buf; 00103 llvm::raw_svector_ostream os(buf); 00104 00105 SmallVector<SourceRange, 2> Ranges; 00106 00107 // Walk through lvalue casts to get the original expression 00108 // that syntactically caused the load. 00109 if (const Expr *expr = dyn_cast<Expr>(S)) 00110 S = expr->IgnoreParenLValueCasts(); 00111 00112 if (IsBind) { 00113 if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(S)) { 00114 if (BO->isAssignmentOp()) 00115 S = BO->getRHS(); 00116 } else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) { 00117 assert(DS->isSingleDecl() && "We process decls one by one"); 00118 if (const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl())) 00119 if (const Expr *Init = VD->getAnyInitializer()) 00120 S = Init; 00121 } 00122 } 00123 00124 switch (S->getStmtClass()) { 00125 case Stmt::ArraySubscriptExprClass: { 00126 os << "Array access"; 00127 const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S); 00128 AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), 00129 State.get(), N->getLocationContext()); 00130 os << " results in a null pointer dereference"; 00131 break; 00132 } 00133 case Stmt::UnaryOperatorClass: { 00134 os << "Dereference of null pointer"; 00135 const UnaryOperator *U = cast<UnaryOperator>(S); 00136 AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(), 00137 State.get(), N->getLocationContext(), true); 00138 break; 00139 } 00140 case Stmt::MemberExprClass: { 00141 const MemberExpr *M = cast<MemberExpr>(S); 00142 if (M->isArrow() || bugreporter::isDeclRefExprToReference(M->getBase())) { 00143 os << "Access to field '" << M->getMemberNameInfo() 00144 << "' results in a dereference of a null pointer"; 00145 AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(), 00146 State.get(), N->getLocationContext(), true); 00147 } 00148 break; 00149 } 00150 case Stmt::ObjCIvarRefExprClass: { 00151 const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S); 00152 os << "Access to instance variable '" << *IV->getDecl() 00153 << "' results in a dereference of a null pointer"; 00154 AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(), 00155 State.get(), N->getLocationContext(), true); 00156 break; 00157 } 00158 default: 00159 break; 00160 } 00161 00162 os.flush(); 00163 BugReport *report = 00164 new BugReport(*BT_null, 00165 buf.empty() ? BT_null->getDescription() : buf.str(), 00166 N); 00167 00168 bugreporter::trackNullOrUndefValue(N, bugreporter::getDerefExpr(S), *report); 00169 00170 for (SmallVectorImpl<SourceRange>::iterator 00171 I = Ranges.begin(), E = Ranges.end(); I!=E; ++I) 00172 report->addRange(*I); 00173 00174 C.emitReport(report); 00175 } 00176 00177 void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, 00178 CheckerContext &C) const { 00179 // Check for dereference of an undefined value. 00180 if (l.isUndef()) { 00181 if (ExplodedNode *N = C.generateSink()) { 00182 if (!BT_undef) 00183 BT_undef.reset( 00184 new BuiltinBug(this, "Dereference of undefined pointer value")); 00185 00186 BugReport *report = 00187 new BugReport(*BT_undef, BT_undef->getDescription(), N); 00188 bugreporter::trackNullOrUndefValue(N, bugreporter::getDerefExpr(S), 00189 *report); 00190 C.emitReport(report); 00191 } 00192 return; 00193 } 00194 00195 DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>(); 00196 00197 // Check for null dereferences. 00198 if (!location.getAs<Loc>()) 00199 return; 00200 00201 ProgramStateRef state = C.getState(); 00202 00203 ProgramStateRef notNullState, nullState; 00204 std::tie(notNullState, nullState) = state->assume(location); 00205 00206 // The explicit NULL case. 00207 if (nullState) { 00208 if (!notNullState) { 00209 reportBug(nullState, S, C); 00210 return; 00211 } 00212 00213 // Otherwise, we have the case where the location could either be 00214 // null or not-null. Record the error node as an "implicit" null 00215 // dereference. 00216 if (ExplodedNode *N = C.generateSink(nullState)) { 00217 ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() }; 00218 dispatchEvent(event); 00219 } 00220 } 00221 00222 // From this point forward, we know that the location is not null. 00223 C.addTransition(notNullState); 00224 } 00225 00226 void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S, 00227 CheckerContext &C) const { 00228 // If we're binding to a reference, check if the value is known to be null. 00229 if (V.isUndef()) 00230 return; 00231 00232 const MemRegion *MR = L.getAsRegion(); 00233 const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR); 00234 if (!TVR) 00235 return; 00236 00237 if (!TVR->getValueType()->isReferenceType()) 00238 return; 00239 00240 ProgramStateRef State = C.getState(); 00241 00242 ProgramStateRef StNonNull, StNull; 00243 std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>()); 00244 00245 if (StNull) { 00246 if (!StNonNull) { 00247 reportBug(StNull, S, C, /*isBind=*/true); 00248 return; 00249 } 00250 00251 // At this point the value could be either null or non-null. 00252 // Record this as an "implicit" null dereference. 00253 if (ExplodedNode *N = C.generateSink(StNull)) { 00254 ImplicitNullDerefEvent event = { V, /*isLoad=*/true, N, 00255 &C.getBugReporter() }; 00256 dispatchEvent(event); 00257 } 00258 } 00259 00260 // Unlike a regular null dereference, initializing a reference with a 00261 // dereferenced null pointer does not actually cause a runtime exception in 00262 // Clang's implementation of references. 00263 // 00264 // int &r = *p; // safe?? 00265 // if (p != NULL) return; // uh-oh 00266 // r = 5; // trap here 00267 // 00268 // The standard says this is invalid as soon as we try to create a "null 00269 // reference" (there is no such thing), but turning this into an assumption 00270 // that 'p' is never null will not match our actual runtime behavior. 00271 // So we do not record this assumption, allowing us to warn on the last line 00272 // of this example. 00273 // 00274 // We do need to add a transition because we may have generated a sink for 00275 // the "implicit" null dereference. 00276 C.addTransition(State, this); 00277 } 00278 00279 void ento::registerDereferenceChecker(CheckerManager &mgr) { 00280 mgr.registerChecker<DereferenceChecker>(); 00281 }