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