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 "ClangSACheckers.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 void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S, 115 CheckerContext &C) const { 116 // Generate an error node. 117 ExplodedNode *N = C.generateErrorNode(State); 118 if (!N) 119 return; 120 121 // We know that 'location' cannot be non-null. This is what 122 // we call an "explicit" null dereference. 123 if (!BT_null) 124 BT_null.reset(new BuiltinBug(this, "Dereference of null pointer")); 125 126 SmallString<100> buf; 127 llvm::raw_svector_ostream os(buf); 128 129 SmallVector<SourceRange, 2> Ranges; 130 131 switch (S->getStmtClass()) { 132 case Stmt::ArraySubscriptExprClass: { 133 os << "Array access"; 134 const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S); 135 AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), 136 State.get(), N->getLocationContext()); 137 os << " results in a null pointer dereference"; 138 break; 139 } 140 case Stmt::OMPArraySectionExprClass: { 141 os << "Array access"; 142 const OMPArraySectionExpr *AE = cast<OMPArraySectionExpr>(S); 143 AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), 144 State.get(), N->getLocationContext()); 145 os << " results in a null pointer dereference"; 146 break; 147 } 148 case Stmt::UnaryOperatorClass: { 149 os << "Dereference of null pointer"; 150 const UnaryOperator *U = cast<UnaryOperator>(S); 151 AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(), 152 State.get(), N->getLocationContext(), true); 153 break; 154 } 155 case Stmt::MemberExprClass: { 156 const MemberExpr *M = cast<MemberExpr>(S); 157 if (M->isArrow() || bugreporter::isDeclRefExprToReference(M->getBase())) { 158 os << "Access to field '" << M->getMemberNameInfo() 159 << "' results in a dereference of a null pointer"; 160 AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(), 161 State.get(), N->getLocationContext(), true); 162 } 163 break; 164 } 165 case Stmt::ObjCIvarRefExprClass: { 166 const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S); 167 os << "Access to instance variable '" << *IV->getDecl() 168 << "' results in a dereference of a null pointer"; 169 AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(), 170 State.get(), N->getLocationContext(), true); 171 break; 172 } 173 default: 174 break; 175 } 176 177 auto report = llvm::make_unique<BugReport>( 178 *BT_null, buf.empty() ? BT_null->getDescription() : StringRef(buf), N); 179 180 bugreporter::trackNullOrUndefValue(N, bugreporter::getDerefExpr(S), *report); 181 182 for (SmallVectorImpl<SourceRange>::iterator 183 I = Ranges.begin(), E = Ranges.end(); I!=E; ++I) 184 report->addRange(*I); 185 186 C.emitReport(std::move(report)); 187 } 188 189 void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, 190 CheckerContext &C) const { 191 // Check for dereference of an undefined value. 192 if (l.isUndef()) { 193 if (ExplodedNode *N = C.generateErrorNode()) { 194 if (!BT_undef) 195 BT_undef.reset( 196 new BuiltinBug(this, "Dereference of undefined pointer value")); 197 198 auto report = 199 llvm::make_unique<BugReport>(*BT_undef, BT_undef->getDescription(), N); 200 bugreporter::trackNullOrUndefValue(N, bugreporter::getDerefExpr(S), 201 *report); 202 C.emitReport(std::move(report)); 203 } 204 return; 205 } 206 207 DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>(); 208 209 // Check for null dereferences. 210 if (!location.getAs<Loc>()) 211 return; 212 213 ProgramStateRef state = C.getState(); 214 215 ProgramStateRef notNullState, nullState; 216 std::tie(notNullState, nullState) = state->assume(location); 217 218 // The explicit NULL case. 219 if (nullState) { 220 if (!notNullState) { 221 const Expr *expr = getDereferenceExpr(S); 222 if (!suppressReport(expr)) { 223 reportBug(nullState, expr, C); 224 return; 225 } 226 } 227 228 // Otherwise, we have the case where the location could either be 229 // null or not-null. Record the error node as an "implicit" null 230 // dereference. 231 if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) { 232 ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(), 233 /*IsDirectDereference=*/true}; 234 dispatchEvent(event); 235 } 236 } 237 238 // From this point forward, we know that the location is not null. 239 C.addTransition(notNullState); 240 } 241 242 void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S, 243 CheckerContext &C) const { 244 // If we're binding to a reference, check if the value is known to be null. 245 if (V.isUndef()) 246 return; 247 248 const MemRegion *MR = L.getAsRegion(); 249 const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR); 250 if (!TVR) 251 return; 252 253 if (!TVR->getValueType()->isReferenceType()) 254 return; 255 256 ProgramStateRef State = C.getState(); 257 258 ProgramStateRef StNonNull, StNull; 259 std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>()); 260 261 if (StNull) { 262 if (!StNonNull) { 263 const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true); 264 if (!suppressReport(expr)) { 265 reportBug(StNull, expr, C); 266 return; 267 } 268 } 269 270 // At this point the value could be either null or non-null. 271 // Record this as an "implicit" null dereference. 272 if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) { 273 ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N, 274 &C.getBugReporter(), 275 /*IsDirectDereference=*/true}; 276 dispatchEvent(event); 277 } 278 } 279 280 // Unlike a regular null dereference, initializing a reference with a 281 // dereferenced null pointer does not actually cause a runtime exception in 282 // Clang's implementation of references. 283 // 284 // int &r = *p; // safe?? 285 // if (p != NULL) return; // uh-oh 286 // r = 5; // trap here 287 // 288 // The standard says this is invalid as soon as we try to create a "null 289 // reference" (there is no such thing), but turning this into an assumption 290 // that 'p' is never null will not match our actual runtime behavior. 291 // So we do not record this assumption, allowing us to warn on the last line 292 // of this example. 293 // 294 // We do need to add a transition because we may have generated a sink for 295 // the "implicit" null dereference. 296 C.addTransition(State, this); 297 } 298 299 void ento::registerDereferenceChecker(CheckerManager &mgr) { 300 mgr.registerChecker<DereferenceChecker>(); 301 } 302