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