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