xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
17330f729Sjoerg //===-- DereferenceChecker.cpp - Null dereference checker -----------------===//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg //
97330f729Sjoerg // This defines NullDerefChecker, a builtin check in ExprEngine that performs
107330f729Sjoerg // checks for null pointers at loads and stores.
117330f729Sjoerg //
127330f729Sjoerg //===----------------------------------------------------------------------===//
137330f729Sjoerg 
147330f729Sjoerg #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
157330f729Sjoerg #include "clang/AST/ExprObjC.h"
167330f729Sjoerg #include "clang/AST/ExprOpenMP.h"
177330f729Sjoerg #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
187330f729Sjoerg #include "clang/StaticAnalyzer/Core/Checker.h"
197330f729Sjoerg #include "clang/StaticAnalyzer/Core/CheckerManager.h"
207330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
217330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
227330f729Sjoerg #include "llvm/ADT/SmallString.h"
237330f729Sjoerg #include "llvm/Support/raw_ostream.h"
247330f729Sjoerg 
257330f729Sjoerg using namespace clang;
267330f729Sjoerg using namespace ento;
277330f729Sjoerg 
287330f729Sjoerg namespace {
297330f729Sjoerg class DereferenceChecker
307330f729Sjoerg     : public Checker< check::Location,
317330f729Sjoerg                       check::Bind,
327330f729Sjoerg                       EventDispatcher<ImplicitNullDerefEvent> > {
33*e038c9c4Sjoerg   enum DerefKind { NullPointer, UndefinedPointerValue };
347330f729Sjoerg 
35*e038c9c4Sjoerg   BugType BT_Null{this, "Dereference of null pointer", categories::LogicError};
36*e038c9c4Sjoerg   BugType BT_Undef{this, "Dereference of undefined pointer value",
37*e038c9c4Sjoerg                    categories::LogicError};
38*e038c9c4Sjoerg 
39*e038c9c4Sjoerg   void reportBug(DerefKind K, ProgramStateRef State, const Stmt *S,
40*e038c9c4Sjoerg                  CheckerContext &C) const;
417330f729Sjoerg 
427330f729Sjoerg public:
437330f729Sjoerg   void checkLocation(SVal location, bool isLoad, const Stmt* S,
447330f729Sjoerg                      CheckerContext &C) const;
457330f729Sjoerg   void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;
467330f729Sjoerg 
477330f729Sjoerg   static void AddDerefSource(raw_ostream &os,
487330f729Sjoerg                              SmallVectorImpl<SourceRange> &Ranges,
497330f729Sjoerg                              const Expr *Ex, const ProgramState *state,
507330f729Sjoerg                              const LocationContext *LCtx,
517330f729Sjoerg                              bool loadedFrom = false);
527330f729Sjoerg };
537330f729Sjoerg } // end anonymous namespace
547330f729Sjoerg 
557330f729Sjoerg void
AddDerefSource(raw_ostream & os,SmallVectorImpl<SourceRange> & Ranges,const Expr * Ex,const ProgramState * state,const LocationContext * LCtx,bool loadedFrom)567330f729Sjoerg DereferenceChecker::AddDerefSource(raw_ostream &os,
577330f729Sjoerg                                    SmallVectorImpl<SourceRange> &Ranges,
587330f729Sjoerg                                    const Expr *Ex,
597330f729Sjoerg                                    const ProgramState *state,
607330f729Sjoerg                                    const LocationContext *LCtx,
617330f729Sjoerg                                    bool loadedFrom) {
627330f729Sjoerg   Ex = Ex->IgnoreParenLValueCasts();
637330f729Sjoerg   switch (Ex->getStmtClass()) {
647330f729Sjoerg     default:
657330f729Sjoerg       break;
667330f729Sjoerg     case Stmt::DeclRefExprClass: {
677330f729Sjoerg       const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);
687330f729Sjoerg       if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
697330f729Sjoerg         os << " (" << (loadedFrom ? "loaded from" : "from")
707330f729Sjoerg            << " variable '" <<  VD->getName() << "')";
717330f729Sjoerg         Ranges.push_back(DR->getSourceRange());
727330f729Sjoerg       }
737330f729Sjoerg       break;
747330f729Sjoerg     }
757330f729Sjoerg     case Stmt::MemberExprClass: {
767330f729Sjoerg       const MemberExpr *ME = cast<MemberExpr>(Ex);
777330f729Sjoerg       os << " (" << (loadedFrom ? "loaded from" : "via")
787330f729Sjoerg          << " field '" << ME->getMemberNameInfo() << "')";
797330f729Sjoerg       SourceLocation L = ME->getMemberLoc();
807330f729Sjoerg       Ranges.push_back(SourceRange(L, L));
817330f729Sjoerg       break;
827330f729Sjoerg     }
837330f729Sjoerg     case Stmt::ObjCIvarRefExprClass: {
847330f729Sjoerg       const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex);
857330f729Sjoerg       os << " (" << (loadedFrom ? "loaded from" : "via")
867330f729Sjoerg          << " ivar '" << IV->getDecl()->getName() << "')";
877330f729Sjoerg       SourceLocation L = IV->getLocation();
887330f729Sjoerg       Ranges.push_back(SourceRange(L, L));
897330f729Sjoerg       break;
907330f729Sjoerg     }
917330f729Sjoerg   }
927330f729Sjoerg }
937330f729Sjoerg 
getDereferenceExpr(const Stmt * S,bool IsBind=false)947330f729Sjoerg static const Expr *getDereferenceExpr(const Stmt *S, bool IsBind=false){
957330f729Sjoerg   const Expr *E = nullptr;
967330f729Sjoerg 
977330f729Sjoerg   // Walk through lvalue casts to get the original expression
987330f729Sjoerg   // that syntactically caused the load.
997330f729Sjoerg   if (const Expr *expr = dyn_cast<Expr>(S))
1007330f729Sjoerg     E = expr->IgnoreParenLValueCasts();
1017330f729Sjoerg 
1027330f729Sjoerg   if (IsBind) {
1037330f729Sjoerg     const VarDecl *VD;
1047330f729Sjoerg     const Expr *Init;
1057330f729Sjoerg     std::tie(VD, Init) = parseAssignment(S);
1067330f729Sjoerg     if (VD && Init)
1077330f729Sjoerg       E = Init;
1087330f729Sjoerg   }
1097330f729Sjoerg   return E;
1107330f729Sjoerg }
1117330f729Sjoerg 
suppressReport(const Expr * E)1127330f729Sjoerg static bool suppressReport(const Expr *E) {
1137330f729Sjoerg   // Do not report dereferences on memory in non-default address spaces.
114*e038c9c4Sjoerg   return E->getType().hasAddressSpace();
1157330f729Sjoerg }
1167330f729Sjoerg 
isDeclRefExprToReference(const Expr * E)1177330f729Sjoerg static bool isDeclRefExprToReference(const Expr *E) {
1187330f729Sjoerg   if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
1197330f729Sjoerg     return DRE->getDecl()->getType()->isReferenceType();
1207330f729Sjoerg   return false;
1217330f729Sjoerg }
1227330f729Sjoerg 
reportBug(DerefKind K,ProgramStateRef State,const Stmt * S,CheckerContext & C) const123*e038c9c4Sjoerg void DereferenceChecker::reportBug(DerefKind K, ProgramStateRef State,
124*e038c9c4Sjoerg                                    const Stmt *S, CheckerContext &C) const {
125*e038c9c4Sjoerg   const BugType *BT = nullptr;
126*e038c9c4Sjoerg   llvm::StringRef DerefStr1;
127*e038c9c4Sjoerg   llvm::StringRef DerefStr2;
128*e038c9c4Sjoerg   switch (K) {
129*e038c9c4Sjoerg   case DerefKind::NullPointer:
130*e038c9c4Sjoerg     BT = &BT_Null;
131*e038c9c4Sjoerg     DerefStr1 = " results in a null pointer dereference";
132*e038c9c4Sjoerg     DerefStr2 = " results in a dereference of a null pointer";
133*e038c9c4Sjoerg     break;
134*e038c9c4Sjoerg   case DerefKind::UndefinedPointerValue:
135*e038c9c4Sjoerg     BT = &BT_Undef;
136*e038c9c4Sjoerg     DerefStr1 = " results in an undefined pointer dereference";
137*e038c9c4Sjoerg     DerefStr2 = " results in a dereference of an undefined pointer value";
138*e038c9c4Sjoerg     break;
139*e038c9c4Sjoerg   };
140*e038c9c4Sjoerg 
1417330f729Sjoerg   // Generate an error node.
1427330f729Sjoerg   ExplodedNode *N = C.generateErrorNode(State);
1437330f729Sjoerg   if (!N)
1447330f729Sjoerg     return;
1457330f729Sjoerg 
1467330f729Sjoerg   SmallString<100> buf;
1477330f729Sjoerg   llvm::raw_svector_ostream os(buf);
1487330f729Sjoerg 
1497330f729Sjoerg   SmallVector<SourceRange, 2> Ranges;
1507330f729Sjoerg 
1517330f729Sjoerg   switch (S->getStmtClass()) {
1527330f729Sjoerg   case Stmt::ArraySubscriptExprClass: {
1537330f729Sjoerg     os << "Array access";
1547330f729Sjoerg     const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
1557330f729Sjoerg     AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
1567330f729Sjoerg                    State.get(), N->getLocationContext());
157*e038c9c4Sjoerg     os << DerefStr1;
1587330f729Sjoerg     break;
1597330f729Sjoerg   }
1607330f729Sjoerg   case Stmt::OMPArraySectionExprClass: {
1617330f729Sjoerg     os << "Array access";
1627330f729Sjoerg     const OMPArraySectionExpr *AE = cast<OMPArraySectionExpr>(S);
1637330f729Sjoerg     AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
1647330f729Sjoerg                    State.get(), N->getLocationContext());
165*e038c9c4Sjoerg     os << DerefStr1;
1667330f729Sjoerg     break;
1677330f729Sjoerg   }
1687330f729Sjoerg   case Stmt::UnaryOperatorClass: {
169*e038c9c4Sjoerg     os << BT->getDescription();
1707330f729Sjoerg     const UnaryOperator *U = cast<UnaryOperator>(S);
1717330f729Sjoerg     AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
1727330f729Sjoerg                    State.get(), N->getLocationContext(), true);
1737330f729Sjoerg     break;
1747330f729Sjoerg   }
1757330f729Sjoerg   case Stmt::MemberExprClass: {
1767330f729Sjoerg     const MemberExpr *M = cast<MemberExpr>(S);
1777330f729Sjoerg     if (M->isArrow() || isDeclRefExprToReference(M->getBase())) {
178*e038c9c4Sjoerg       os << "Access to field '" << M->getMemberNameInfo() << "'" << DerefStr2;
1797330f729Sjoerg       AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
1807330f729Sjoerg                      State.get(), N->getLocationContext(), true);
1817330f729Sjoerg     }
1827330f729Sjoerg     break;
1837330f729Sjoerg   }
1847330f729Sjoerg   case Stmt::ObjCIvarRefExprClass: {
1857330f729Sjoerg     const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
186*e038c9c4Sjoerg     os << "Access to instance variable '" << *IV->getDecl() << "'" << DerefStr2;
1877330f729Sjoerg     AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(),
1887330f729Sjoerg                    State.get(), N->getLocationContext(), true);
1897330f729Sjoerg     break;
1907330f729Sjoerg   }
1917330f729Sjoerg   default:
1927330f729Sjoerg     break;
1937330f729Sjoerg   }
1947330f729Sjoerg 
1957330f729Sjoerg   auto report = std::make_unique<PathSensitiveBugReport>(
196*e038c9c4Sjoerg       *BT, buf.empty() ? BT->getDescription() : StringRef(buf), N);
1977330f729Sjoerg 
1987330f729Sjoerg   bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report);
1997330f729Sjoerg 
2007330f729Sjoerg   for (SmallVectorImpl<SourceRange>::iterator
2017330f729Sjoerg        I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
2027330f729Sjoerg     report->addRange(*I);
2037330f729Sjoerg 
2047330f729Sjoerg   C.emitReport(std::move(report));
2057330f729Sjoerg }
2067330f729Sjoerg 
checkLocation(SVal l,bool isLoad,const Stmt * S,CheckerContext & C) const2077330f729Sjoerg void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
2087330f729Sjoerg                                        CheckerContext &C) const {
2097330f729Sjoerg   // Check for dereference of an undefined value.
2107330f729Sjoerg   if (l.isUndef()) {
211*e038c9c4Sjoerg     const Expr *DerefExpr = getDereferenceExpr(S);
212*e038c9c4Sjoerg     if (!suppressReport(DerefExpr))
213*e038c9c4Sjoerg       reportBug(DerefKind::UndefinedPointerValue, C.getState(), DerefExpr, C);
2147330f729Sjoerg     return;
2157330f729Sjoerg   }
2167330f729Sjoerg 
2177330f729Sjoerg   DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>();
2187330f729Sjoerg 
2197330f729Sjoerg   // Check for null dereferences.
2207330f729Sjoerg   if (!location.getAs<Loc>())
2217330f729Sjoerg     return;
2227330f729Sjoerg 
2237330f729Sjoerg   ProgramStateRef state = C.getState();
2247330f729Sjoerg 
2257330f729Sjoerg   ProgramStateRef notNullState, nullState;
2267330f729Sjoerg   std::tie(notNullState, nullState) = state->assume(location);
2277330f729Sjoerg 
2287330f729Sjoerg   if (nullState) {
2297330f729Sjoerg     if (!notNullState) {
230*e038c9c4Sjoerg       // We know that 'location' can only be null.  This is what
231*e038c9c4Sjoerg       // we call an "explicit" null dereference.
2327330f729Sjoerg       const Expr *expr = getDereferenceExpr(S);
2337330f729Sjoerg       if (!suppressReport(expr)) {
234*e038c9c4Sjoerg         reportBug(DerefKind::NullPointer, nullState, expr, C);
2357330f729Sjoerg         return;
2367330f729Sjoerg       }
2377330f729Sjoerg     }
2387330f729Sjoerg 
2397330f729Sjoerg     // Otherwise, we have the case where the location could either be
2407330f729Sjoerg     // null or not-null.  Record the error node as an "implicit" null
2417330f729Sjoerg     // dereference.
2427330f729Sjoerg     if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) {
2437330f729Sjoerg       ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(),
2447330f729Sjoerg                                       /*IsDirectDereference=*/true};
2457330f729Sjoerg       dispatchEvent(event);
2467330f729Sjoerg     }
2477330f729Sjoerg   }
2487330f729Sjoerg 
2497330f729Sjoerg   // From this point forward, we know that the location is not null.
2507330f729Sjoerg   C.addTransition(notNullState);
2517330f729Sjoerg }
2527330f729Sjoerg 
checkBind(SVal L,SVal V,const Stmt * S,CheckerContext & C) const2537330f729Sjoerg void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
2547330f729Sjoerg                                    CheckerContext &C) const {
2557330f729Sjoerg   // If we're binding to a reference, check if the value is known to be null.
2567330f729Sjoerg   if (V.isUndef())
2577330f729Sjoerg     return;
2587330f729Sjoerg 
2597330f729Sjoerg   const MemRegion *MR = L.getAsRegion();
2607330f729Sjoerg   const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
2617330f729Sjoerg   if (!TVR)
2627330f729Sjoerg     return;
2637330f729Sjoerg 
2647330f729Sjoerg   if (!TVR->getValueType()->isReferenceType())
2657330f729Sjoerg     return;
2667330f729Sjoerg 
2677330f729Sjoerg   ProgramStateRef State = C.getState();
2687330f729Sjoerg 
2697330f729Sjoerg   ProgramStateRef StNonNull, StNull;
2707330f729Sjoerg   std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
2717330f729Sjoerg 
2727330f729Sjoerg   if (StNull) {
2737330f729Sjoerg     if (!StNonNull) {
2747330f729Sjoerg       const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true);
2757330f729Sjoerg       if (!suppressReport(expr)) {
276*e038c9c4Sjoerg         reportBug(DerefKind::NullPointer, StNull, expr, C);
2777330f729Sjoerg         return;
2787330f729Sjoerg       }
2797330f729Sjoerg     }
2807330f729Sjoerg 
2817330f729Sjoerg     // At this point the value could be either null or non-null.
2827330f729Sjoerg     // Record this as an "implicit" null dereference.
2837330f729Sjoerg     if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) {
2847330f729Sjoerg       ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N,
2857330f729Sjoerg                                       &C.getBugReporter(),
2867330f729Sjoerg                                       /*IsDirectDereference=*/true};
2877330f729Sjoerg       dispatchEvent(event);
2887330f729Sjoerg     }
2897330f729Sjoerg   }
2907330f729Sjoerg 
2917330f729Sjoerg   // Unlike a regular null dereference, initializing a reference with a
2927330f729Sjoerg   // dereferenced null pointer does not actually cause a runtime exception in
2937330f729Sjoerg   // Clang's implementation of references.
2947330f729Sjoerg   //
2957330f729Sjoerg   //   int &r = *p; // safe??
2967330f729Sjoerg   //   if (p != NULL) return; // uh-oh
2977330f729Sjoerg   //   r = 5; // trap here
2987330f729Sjoerg   //
2997330f729Sjoerg   // The standard says this is invalid as soon as we try to create a "null
3007330f729Sjoerg   // reference" (there is no such thing), but turning this into an assumption
3017330f729Sjoerg   // that 'p' is never null will not match our actual runtime behavior.
3027330f729Sjoerg   // So we do not record this assumption, allowing us to warn on the last line
3037330f729Sjoerg   // of this example.
3047330f729Sjoerg   //
3057330f729Sjoerg   // We do need to add a transition because we may have generated a sink for
3067330f729Sjoerg   // the "implicit" null dereference.
3077330f729Sjoerg   C.addTransition(State, this);
3087330f729Sjoerg }
3097330f729Sjoerg 
registerDereferenceChecker(CheckerManager & mgr)3107330f729Sjoerg void ento::registerDereferenceChecker(CheckerManager &mgr) {
3117330f729Sjoerg   mgr.registerChecker<DereferenceChecker>();
3127330f729Sjoerg }
3137330f729Sjoerg 
shouldRegisterDereferenceChecker(const CheckerManager & mgr)314*e038c9c4Sjoerg bool ento::shouldRegisterDereferenceChecker(const CheckerManager &mgr) {
3157330f729Sjoerg   return true;
3167330f729Sjoerg }
317