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