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/AST/ExprObjC.h" 15 #include "clang/AST/ExprOpenMP.h" 16 #include "clang/Basic/TargetInfo.h" 17 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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 enum DerefKind { NullPointer, UndefinedPointerValue, AddressOfLabel }; 35 36 void reportBug(DerefKind K, ProgramStateRef State, const Stmt *S, 37 CheckerContext &C) const; 38 39 bool suppressReport(CheckerContext &C, const Expr *E) const; 40 41 public: 42 void checkLocation(SVal location, bool isLoad, const Stmt* S, 43 CheckerContext &C) const; 44 void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const; 45 46 static void AddDerefSource(raw_ostream &os, 47 SmallVectorImpl<SourceRange> &Ranges, 48 const Expr *Ex, const ProgramState *state, 49 const LocationContext *LCtx, 50 bool loadedFrom = false); 51 52 bool SuppressAddressSpaces = false; 53 54 bool CheckNullDereference = false; 55 56 std::unique_ptr<BugType> BT_Null; 57 std::unique_ptr<BugType> BT_Undef; 58 std::unique_ptr<BugType> BT_Label; 59 }; 60 } // end anonymous namespace 61 62 void 63 DereferenceChecker::AddDerefSource(raw_ostream &os, 64 SmallVectorImpl<SourceRange> &Ranges, 65 const Expr *Ex, 66 const ProgramState *state, 67 const LocationContext *LCtx, 68 bool loadedFrom) { 69 Ex = Ex->IgnoreParenLValueCasts(); 70 switch (Ex->getStmtClass()) { 71 default: 72 break; 73 case Stmt::DeclRefExprClass: { 74 const DeclRefExpr *DR = cast<DeclRefExpr>(Ex); 75 if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { 76 os << " (" << (loadedFrom ? "loaded from" : "from") 77 << " variable '" << VD->getName() << "')"; 78 Ranges.push_back(DR->getSourceRange()); 79 } 80 break; 81 } 82 case Stmt::MemberExprClass: { 83 const MemberExpr *ME = cast<MemberExpr>(Ex); 84 os << " (" << (loadedFrom ? "loaded from" : "via") 85 << " field '" << ME->getMemberNameInfo() << "')"; 86 SourceLocation L = ME->getMemberLoc(); 87 Ranges.push_back(SourceRange(L, L)); 88 break; 89 } 90 case Stmt::ObjCIvarRefExprClass: { 91 const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex); 92 os << " (" << (loadedFrom ? "loaded from" : "via") 93 << " ivar '" << IV->getDecl()->getName() << "')"; 94 SourceLocation L = IV->getLocation(); 95 Ranges.push_back(SourceRange(L, L)); 96 break; 97 } 98 } 99 } 100 101 static const Expr *getDereferenceExpr(const Stmt *S, bool IsBind=false){ 102 const Expr *E = nullptr; 103 104 // Walk through lvalue casts to get the original expression 105 // that syntactically caused the load. 106 if (const Expr *expr = dyn_cast<Expr>(S)) 107 E = expr->IgnoreParenLValueCasts(); 108 109 if (IsBind) { 110 const VarDecl *VD; 111 const Expr *Init; 112 std::tie(VD, Init) = parseAssignment(S); 113 if (VD && Init) 114 E = Init; 115 } 116 return E; 117 } 118 119 bool DereferenceChecker::suppressReport(CheckerContext &C, 120 const Expr *E) const { 121 // Do not report dereferences on memory that use address space #256, #257, 122 // and #258. Those address spaces are used when dereferencing address spaces 123 // relative to the GS, FS, and SS segments on x86/x86-64 targets. 124 // Dereferencing a null pointer in these address spaces is not defined 125 // as an error. All other null dereferences in other address spaces 126 // are defined as an error unless explicitly defined. 127 // See https://clang.llvm.org/docs/LanguageExtensions.html, the section 128 // "X86/X86-64 Language Extensions" 129 130 QualType Ty = E->getType(); 131 if (!Ty.hasAddressSpace()) 132 return false; 133 if (SuppressAddressSpaces) 134 return true; 135 136 const llvm::Triple::ArchType Arch = 137 C.getASTContext().getTargetInfo().getTriple().getArch(); 138 139 if ((Arch == llvm::Triple::x86) || (Arch == llvm::Triple::x86_64)) { 140 switch (toTargetAddressSpace(E->getType().getAddressSpace())) { 141 case 256: 142 case 257: 143 case 258: 144 return true; 145 } 146 } 147 return false; 148 } 149 150 static bool isDeclRefExprToReference(const Expr *E) { 151 if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) 152 return DRE->getDecl()->getType()->isReferenceType(); 153 return false; 154 } 155 156 void DereferenceChecker::reportBug(DerefKind K, ProgramStateRef State, 157 const Stmt *S, CheckerContext &C) const { 158 if (!CheckNullDereference) { 159 C.addSink(); 160 return; 161 } 162 163 const BugType *BT = nullptr; 164 llvm::StringRef DerefStr1; 165 llvm::StringRef DerefStr2; 166 switch (K) { 167 case DerefKind::NullPointer: 168 BT = BT_Null.get(); 169 DerefStr1 = " results in a null pointer dereference"; 170 DerefStr2 = " results in a dereference of a null pointer"; 171 break; 172 case DerefKind::UndefinedPointerValue: 173 BT = BT_Undef.get(); 174 DerefStr1 = " results in an undefined pointer dereference"; 175 DerefStr2 = " results in a dereference of an undefined pointer value"; 176 break; 177 case DerefKind::AddressOfLabel: 178 BT = BT_Label.get(); 179 DerefStr1 = " results in an undefined pointer dereference"; 180 DerefStr2 = " results in a dereference of an address of a label"; 181 break; 182 }; 183 184 // Generate an error node. 185 ExplodedNode *N = C.generateErrorNode(State); 186 if (!N) 187 return; 188 189 SmallString<100> buf; 190 llvm::raw_svector_ostream os(buf); 191 192 SmallVector<SourceRange, 2> Ranges; 193 194 switch (S->getStmtClass()) { 195 case Stmt::ArraySubscriptExprClass: { 196 os << "Array access"; 197 const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S); 198 AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), 199 State.get(), N->getLocationContext()); 200 os << DerefStr1; 201 break; 202 } 203 case Stmt::ArraySectionExprClass: { 204 os << "Array access"; 205 const ArraySectionExpr *AE = cast<ArraySectionExpr>(S); 206 AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), 207 State.get(), N->getLocationContext()); 208 os << DerefStr1; 209 break; 210 } 211 case Stmt::UnaryOperatorClass: { 212 os << BT->getDescription(); 213 const UnaryOperator *U = cast<UnaryOperator>(S); 214 AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(), 215 State.get(), N->getLocationContext(), true); 216 break; 217 } 218 case Stmt::MemberExprClass: { 219 const MemberExpr *M = cast<MemberExpr>(S); 220 if (M->isArrow() || isDeclRefExprToReference(M->getBase())) { 221 os << "Access to field '" << M->getMemberNameInfo() << "'" << DerefStr2; 222 AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(), 223 State.get(), N->getLocationContext(), true); 224 } 225 break; 226 } 227 case Stmt::ObjCIvarRefExprClass: { 228 const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S); 229 os << "Access to instance variable '" << *IV->getDecl() << "'" << DerefStr2; 230 AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(), 231 State.get(), N->getLocationContext(), true); 232 break; 233 } 234 default: 235 break; 236 } 237 238 auto report = std::make_unique<PathSensitiveBugReport>( 239 *BT, buf.empty() ? BT->getDescription() : buf.str(), N); 240 241 bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report); 242 243 for (SmallVectorImpl<SourceRange>::iterator 244 I = Ranges.begin(), E = Ranges.end(); I!=E; ++I) 245 report->addRange(*I); 246 247 C.emitReport(std::move(report)); 248 } 249 250 void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, 251 CheckerContext &C) const { 252 // Check for dereference of an undefined value. 253 if (l.isUndef()) { 254 const Expr *DerefExpr = getDereferenceExpr(S); 255 if (!suppressReport(C, DerefExpr)) 256 reportBug(DerefKind::UndefinedPointerValue, C.getState(), DerefExpr, C); 257 return; 258 } 259 260 DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>(); 261 262 // Check for null dereferences. 263 if (!isa<Loc>(location)) 264 return; 265 266 ProgramStateRef state = C.getState(); 267 268 ProgramStateRef notNullState, nullState; 269 std::tie(notNullState, nullState) = state->assume(location); 270 271 if (nullState) { 272 if (!notNullState) { 273 // We know that 'location' can only be null. This is what 274 // we call an "explicit" null dereference. 275 const Expr *expr = getDereferenceExpr(S); 276 if (!suppressReport(C, expr)) { 277 reportBug(DerefKind::NullPointer, nullState, expr, C); 278 return; 279 } 280 } 281 282 // Otherwise, we have the case where the location could either be 283 // null or not-null. Record the error node as an "implicit" null 284 // dereference. 285 if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) { 286 ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(), 287 /*IsDirectDereference=*/true}; 288 dispatchEvent(event); 289 } 290 } 291 292 // From this point forward, we know that the location is not null. 293 C.addTransition(notNullState); 294 } 295 296 void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S, 297 CheckerContext &C) const { 298 // If we're binding to a reference, check if the value is known to be null. 299 if (V.isUndef()) 300 return; 301 302 // One should never write to label addresses. 303 if (auto Label = L.getAs<loc::GotoLabel>()) { 304 reportBug(DerefKind::AddressOfLabel, C.getState(), S, C); 305 return; 306 } 307 308 const MemRegion *MR = L.getAsRegion(); 309 const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR); 310 if (!TVR) 311 return; 312 313 if (!TVR->getValueType()->isReferenceType()) 314 return; 315 316 ProgramStateRef State = C.getState(); 317 318 ProgramStateRef StNonNull, StNull; 319 std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>()); 320 321 if (StNull) { 322 if (!StNonNull) { 323 const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true); 324 if (!suppressReport(C, expr)) { 325 reportBug(DerefKind::NullPointer, StNull, expr, C); 326 return; 327 } 328 } 329 330 // At this point the value could be either null or non-null. 331 // Record this as an "implicit" null dereference. 332 if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) { 333 ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N, 334 &C.getBugReporter(), 335 /*IsDirectDereference=*/true}; 336 dispatchEvent(event); 337 } 338 } 339 340 // Unlike a regular null dereference, initializing a reference with a 341 // dereferenced null pointer does not actually cause a runtime exception in 342 // Clang's implementation of references. 343 // 344 // int &r = *p; // safe?? 345 // if (p != NULL) return; // uh-oh 346 // r = 5; // trap here 347 // 348 // The standard says this is invalid as soon as we try to create a "null 349 // reference" (there is no such thing), but turning this into an assumption 350 // that 'p' is never null will not match our actual runtime behavior. 351 // So we do not record this assumption, allowing us to warn on the last line 352 // of this example. 353 // 354 // We do need to add a transition because we may have generated a sink for 355 // the "implicit" null dereference. 356 C.addTransition(State, this); 357 } 358 359 void ento::registerDereferenceModeling(CheckerManager &Mgr) { 360 Mgr.registerChecker<DereferenceChecker>(); 361 } 362 363 bool ento::shouldRegisterDereferenceModeling(const CheckerManager &) { 364 return true; 365 } 366 367 void ento::registerNullDereferenceChecker(CheckerManager &Mgr) { 368 auto *Chk = Mgr.getChecker<DereferenceChecker>(); 369 Chk->CheckNullDereference = true; 370 Chk->SuppressAddressSpaces = Mgr.getAnalyzerOptions().getCheckerBooleanOption( 371 Mgr.getCurrentCheckerName(), "SuppressAddressSpaces"); 372 Chk->BT_Null.reset(new BugType(Mgr.getCurrentCheckerName(), 373 "Dereference of null pointer", 374 categories::LogicError)); 375 Chk->BT_Undef.reset(new BugType(Mgr.getCurrentCheckerName(), 376 "Dereference of undefined pointer value", 377 categories::LogicError)); 378 Chk->BT_Label.reset(new BugType(Mgr.getCurrentCheckerName(), 379 "Dereference of the address of a label", 380 categories::LogicError)); 381 } 382 383 bool ento::shouldRegisterNullDereferenceChecker(const CheckerManager &) { 384 return true; 385 } 386