1 //===-- DereferenceChecker.cpp - Null dereference checker -----------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This defines NullDerefChecker, a builtin check in ExprEngine that performs 10 // checks for null pointers at loads and stores. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 15 #include "clang/AST/ExprObjC.h" 16 #include "clang/AST/ExprOpenMP.h" 17 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 18 #include "clang/StaticAnalyzer/Core/Checker.h" 19 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 20 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 21 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" 22 #include "llvm/ADT/SmallString.h" 23 #include "llvm/Support/raw_ostream.h" 24 25 using namespace clang; 26 using namespace ento; 27 28 namespace { 29 class DereferenceChecker 30 : public Checker< check::Location, 31 check::Bind, 32 EventDispatcher<ImplicitNullDerefEvent> > { 33 BugType BT_Null{this, "Dereference of null pointer", categories::LogicError}; 34 BugType BT_Undef{this, "Dereference of undefined pointer value", 35 categories::LogicError}; 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().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 SmallString<100> buf; 128 llvm::raw_svector_ostream os(buf); 129 130 SmallVector<SourceRange, 2> Ranges; 131 132 switch (S->getStmtClass()) { 133 case Stmt::ArraySubscriptExprClass: { 134 os << "Array access"; 135 const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S); 136 AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), 137 State.get(), N->getLocationContext()); 138 os << " results in a null pointer dereference"; 139 break; 140 } 141 case Stmt::OMPArraySectionExprClass: { 142 os << "Array access"; 143 const OMPArraySectionExpr *AE = cast<OMPArraySectionExpr>(S); 144 AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), 145 State.get(), N->getLocationContext()); 146 os << " results in a null pointer dereference"; 147 break; 148 } 149 case Stmt::UnaryOperatorClass: { 150 os << "Dereference of null pointer"; 151 const UnaryOperator *U = cast<UnaryOperator>(S); 152 AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(), 153 State.get(), N->getLocationContext(), true); 154 break; 155 } 156 case Stmt::MemberExprClass: { 157 const MemberExpr *M = cast<MemberExpr>(S); 158 if (M->isArrow() || isDeclRefExprToReference(M->getBase())) { 159 os << "Access to field '" << M->getMemberNameInfo() 160 << "' results in a dereference of a null pointer"; 161 AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(), 162 State.get(), N->getLocationContext(), true); 163 } 164 break; 165 } 166 case Stmt::ObjCIvarRefExprClass: { 167 const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S); 168 os << "Access to instance variable '" << *IV->getDecl() 169 << "' results in a dereference of a null pointer"; 170 AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(), 171 State.get(), N->getLocationContext(), true); 172 break; 173 } 174 default: 175 break; 176 } 177 178 auto report = std::make_unique<PathSensitiveBugReport>( 179 BT_Null, buf.empty() ? BT_Null.getDescription() : StringRef(buf), N); 180 181 bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report); 182 183 for (SmallVectorImpl<SourceRange>::iterator 184 I = Ranges.begin(), E = Ranges.end(); I!=E; ++I) 185 report->addRange(*I); 186 187 C.emitReport(std::move(report)); 188 } 189 190 void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, 191 CheckerContext &C) const { 192 // Check for dereference of an undefined value. 193 if (l.isUndef()) { 194 if (ExplodedNode *N = C.generateErrorNode()) { 195 auto report = std::make_unique<PathSensitiveBugReport>( 196 BT_Undef, BT_Undef.getDescription(), N); 197 bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report); 198 C.emitReport(std::move(report)); 199 } 200 return; 201 } 202 203 DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>(); 204 205 // Check for null dereferences. 206 if (!location.getAs<Loc>()) 207 return; 208 209 ProgramStateRef state = C.getState(); 210 211 ProgramStateRef notNullState, nullState; 212 std::tie(notNullState, nullState) = state->assume(location); 213 214 if (nullState) { 215 if (!notNullState) { 216 // We know that 'location' can only be null. This is what 217 // we call an "explicit" null dereference. 218 const Expr *expr = getDereferenceExpr(S); 219 if (!suppressReport(expr)) { 220 reportBug(nullState, expr, C); 221 return; 222 } 223 } 224 225 // Otherwise, we have the case where the location could either be 226 // null or not-null. Record the error node as an "implicit" null 227 // dereference. 228 if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) { 229 ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(), 230 /*IsDirectDereference=*/true}; 231 dispatchEvent(event); 232 } 233 } 234 235 // From this point forward, we know that the location is not null. 236 C.addTransition(notNullState); 237 } 238 239 void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S, 240 CheckerContext &C) const { 241 // If we're binding to a reference, check if the value is known to be null. 242 if (V.isUndef()) 243 return; 244 245 const MemRegion *MR = L.getAsRegion(); 246 const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR); 247 if (!TVR) 248 return; 249 250 if (!TVR->getValueType()->isReferenceType()) 251 return; 252 253 ProgramStateRef State = C.getState(); 254 255 ProgramStateRef StNonNull, StNull; 256 std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>()); 257 258 if (StNull) { 259 if (!StNonNull) { 260 const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true); 261 if (!suppressReport(expr)) { 262 reportBug(StNull, expr, C); 263 return; 264 } 265 } 266 267 // At this point the value could be either null or non-null. 268 // Record this as an "implicit" null dereference. 269 if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) { 270 ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N, 271 &C.getBugReporter(), 272 /*IsDirectDereference=*/true}; 273 dispatchEvent(event); 274 } 275 } 276 277 // Unlike a regular null dereference, initializing a reference with a 278 // dereferenced null pointer does not actually cause a runtime exception in 279 // Clang's implementation of references. 280 // 281 // int &r = *p; // safe?? 282 // if (p != NULL) return; // uh-oh 283 // r = 5; // trap here 284 // 285 // The standard says this is invalid as soon as we try to create a "null 286 // reference" (there is no such thing), but turning this into an assumption 287 // that 'p' is never null will not match our actual runtime behavior. 288 // So we do not record this assumption, allowing us to warn on the last line 289 // of this example. 290 // 291 // We do need to add a transition because we may have generated a sink for 292 // the "implicit" null dereference. 293 C.addTransition(State, this); 294 } 295 296 void ento::registerDereferenceChecker(CheckerManager &mgr) { 297 mgr.registerChecker<DereferenceChecker>(); 298 } 299 300 bool ento::shouldRegisterDereferenceChecker(const CheckerManager &mgr) { 301 return true; 302 } 303