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 "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 16 #include "clang/AST/ExprObjC.h" 17 #include "clang/AST/ExprOpenMP.h" 18 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 19 #include "clang/StaticAnalyzer/Core/Checker.h" 20 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 21 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 22 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" 23 #include "llvm/ADT/SmallString.h" 24 #include "llvm/Support/raw_ostream.h" 25 26 using namespace clang; 27 using namespace ento; 28 29 namespace { 30 class DereferenceChecker 31 : public Checker< check::Location, 32 check::Bind, 33 EventDispatcher<ImplicitNullDerefEvent> > { 34 mutable std::unique_ptr<BuiltinBug> BT_null; 35 mutable std::unique_ptr<BuiltinBug> BT_undef; 36 37 void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C) const; 38 39 public: 40 void checkLocation(SVal location, bool isLoad, const Stmt* S, 41 CheckerContext &C) const; 42 void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const; 43 44 static void AddDerefSource(raw_ostream &os, 45 SmallVectorImpl<SourceRange> &Ranges, 46 const Expr *Ex, const ProgramState *state, 47 const LocationContext *LCtx, 48 bool loadedFrom = false); 49 }; 50 } // end anonymous namespace 51 52 void 53 DereferenceChecker::AddDerefSource(raw_ostream &os, 54 SmallVectorImpl<SourceRange> &Ranges, 55 const Expr *Ex, 56 const ProgramState *state, 57 const LocationContext *LCtx, 58 bool loadedFrom) { 59 Ex = Ex->IgnoreParenLValueCasts(); 60 switch (Ex->getStmtClass()) { 61 default: 62 break; 63 case Stmt::DeclRefExprClass: { 64 const DeclRefExpr *DR = cast<DeclRefExpr>(Ex); 65 if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { 66 os << " (" << (loadedFrom ? "loaded from" : "from") 67 << " variable '" << VD->getName() << "')"; 68 Ranges.push_back(DR->getSourceRange()); 69 } 70 break; 71 } 72 case Stmt::MemberExprClass: { 73 const MemberExpr *ME = cast<MemberExpr>(Ex); 74 os << " (" << (loadedFrom ? "loaded from" : "via") 75 << " field '" << ME->getMemberNameInfo() << "')"; 76 SourceLocation L = ME->getMemberLoc(); 77 Ranges.push_back(SourceRange(L, L)); 78 break; 79 } 80 case Stmt::ObjCIvarRefExprClass: { 81 const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex); 82 os << " (" << (loadedFrom ? "loaded from" : "via") 83 << " ivar '" << IV->getDecl()->getName() << "')"; 84 SourceLocation L = IV->getLocation(); 85 Ranges.push_back(SourceRange(L, L)); 86 break; 87 } 88 } 89 } 90 91 static const Expr *getDereferenceExpr(const Stmt *S, bool IsBind=false){ 92 const Expr *E = nullptr; 93 94 // Walk through lvalue casts to get the original expression 95 // that syntactically caused the load. 96 if (const Expr *expr = dyn_cast<Expr>(S)) 97 E = expr->IgnoreParenLValueCasts(); 98 99 if (IsBind) { 100 const VarDecl *VD; 101 const Expr *Init; 102 std::tie(VD, Init) = parseAssignment(S); 103 if (VD && Init) 104 E = Init; 105 } 106 return E; 107 } 108 109 static bool suppressReport(const Expr *E) { 110 // Do not report dereferences on memory in non-default address spaces. 111 return E->getType().getQualifiers().hasAddressSpace(); 112 } 113 114 static bool isDeclRefExprToReference(const Expr *E) { 115 if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) 116 return DRE->getDecl()->getType()->isReferenceType(); 117 return false; 118 } 119 120 void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S, 121 CheckerContext &C) const { 122 // Generate an error node. 123 ExplodedNode *N = C.generateErrorNode(State); 124 if (!N) 125 return; 126 127 // We know that 'location' cannot be non-null. This is what 128 // we call an "explicit" null dereference. 129 if (!BT_null) 130 BT_null.reset(new BuiltinBug(this, "Dereference of null pointer")); 131 132 SmallString<100> buf; 133 llvm::raw_svector_ostream os(buf); 134 135 SmallVector<SourceRange, 2> Ranges; 136 137 switch (S->getStmtClass()) { 138 case Stmt::ArraySubscriptExprClass: { 139 os << "Array access"; 140 const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S); 141 AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), 142 State.get(), N->getLocationContext()); 143 os << " results in a null pointer dereference"; 144 break; 145 } 146 case Stmt::OMPArraySectionExprClass: { 147 os << "Array access"; 148 const OMPArraySectionExpr *AE = cast<OMPArraySectionExpr>(S); 149 AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), 150 State.get(), N->getLocationContext()); 151 os << " results in a null pointer dereference"; 152 break; 153 } 154 case Stmt::UnaryOperatorClass: { 155 os << "Dereference of null pointer"; 156 const UnaryOperator *U = cast<UnaryOperator>(S); 157 AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(), 158 State.get(), N->getLocationContext(), true); 159 break; 160 } 161 case Stmt::MemberExprClass: { 162 const MemberExpr *M = cast<MemberExpr>(S); 163 if (M->isArrow() || isDeclRefExprToReference(M->getBase())) { 164 os << "Access to field '" << M->getMemberNameInfo() 165 << "' results in a dereference of a null pointer"; 166 AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(), 167 State.get(), N->getLocationContext(), true); 168 } 169 break; 170 } 171 case Stmt::ObjCIvarRefExprClass: { 172 const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S); 173 os << "Access to instance variable '" << *IV->getDecl() 174 << "' results in a dereference of a null pointer"; 175 AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(), 176 State.get(), N->getLocationContext(), true); 177 break; 178 } 179 default: 180 break; 181 } 182 183 auto report = llvm::make_unique<BugReport>( 184 *BT_null, buf.empty() ? BT_null->getDescription() : StringRef(buf), N); 185 186 bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report); 187 188 for (SmallVectorImpl<SourceRange>::iterator 189 I = Ranges.begin(), E = Ranges.end(); I!=E; ++I) 190 report->addRange(*I); 191 192 C.emitReport(std::move(report)); 193 } 194 195 void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, 196 CheckerContext &C) const { 197 // Check for dereference of an undefined value. 198 if (l.isUndef()) { 199 if (ExplodedNode *N = C.generateErrorNode()) { 200 if (!BT_undef) 201 BT_undef.reset( 202 new BuiltinBug(this, "Dereference of undefined pointer value")); 203 204 auto report = 205 llvm::make_unique<BugReport>(*BT_undef, BT_undef->getDescription(), N); 206 bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report); 207 C.emitReport(std::move(report)); 208 } 209 return; 210 } 211 212 DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>(); 213 214 // Check for null dereferences. 215 if (!location.getAs<Loc>()) 216 return; 217 218 ProgramStateRef state = C.getState(); 219 220 ProgramStateRef notNullState, nullState; 221 std::tie(notNullState, nullState) = state->assume(location); 222 223 // The explicit NULL case. 224 if (nullState) { 225 if (!notNullState) { 226 const Expr *expr = getDereferenceExpr(S); 227 if (!suppressReport(expr)) { 228 reportBug(nullState, expr, C); 229 return; 230 } 231 } 232 233 // Otherwise, we have the case where the location could either be 234 // null or not-null. Record the error node as an "implicit" null 235 // dereference. 236 if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) { 237 ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(), 238 /*IsDirectDereference=*/true}; 239 dispatchEvent(event); 240 } 241 } 242 243 // From this point forward, we know that the location is not null. 244 C.addTransition(notNullState); 245 } 246 247 void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S, 248 CheckerContext &C) const { 249 // If we're binding to a reference, check if the value is known to be null. 250 if (V.isUndef()) 251 return; 252 253 const MemRegion *MR = L.getAsRegion(); 254 const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR); 255 if (!TVR) 256 return; 257 258 if (!TVR->getValueType()->isReferenceType()) 259 return; 260 261 ProgramStateRef State = C.getState(); 262 263 ProgramStateRef StNonNull, StNull; 264 std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>()); 265 266 if (StNull) { 267 if (!StNonNull) { 268 const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true); 269 if (!suppressReport(expr)) { 270 reportBug(StNull, expr, C); 271 return; 272 } 273 } 274 275 // At this point the value could be either null or non-null. 276 // Record this as an "implicit" null dereference. 277 if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) { 278 ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N, 279 &C.getBugReporter(), 280 /*IsDirectDereference=*/true}; 281 dispatchEvent(event); 282 } 283 } 284 285 // Unlike a regular null dereference, initializing a reference with a 286 // dereferenced null pointer does not actually cause a runtime exception in 287 // Clang's implementation of references. 288 // 289 // int &r = *p; // safe?? 290 // if (p != NULL) return; // uh-oh 291 // r = 5; // trap here 292 // 293 // The standard says this is invalid as soon as we try to create a "null 294 // reference" (there is no such thing), but turning this into an assumption 295 // that 'p' is never null will not match our actual runtime behavior. 296 // So we do not record this assumption, allowing us to warn on the last line 297 // of this example. 298 // 299 // We do need to add a transition because we may have generated a sink for 300 // the "implicit" null dereference. 301 C.addTransition(State, this); 302 } 303 304 void ento::registerDereferenceChecker(CheckerManager &mgr) { 305 mgr.registerChecker<DereferenceChecker>(); 306 } 307