head	1.1;
branch	1.1.1;
access;
symbols
	netbsd-9-5-RELEASE:1.1.1.6
	netbsd-11-0-RELEASE:1.1.1.7
	netbsd-11-0-RC7:1.1.1.7
	netbsd-11-0-RC6:1.1.1.7
	netbsd-11-0-RC5:1.1.1.7
	netbsd-11-0-RC4:1.1.1.7
	netbsd-11-0-RC3:1.1.1.7
	netbsd-11-0-RC2:1.1.1.7
	netbsd-11-0-RC1:1.1.1.7
	perseant-exfatfs-base-20250801:1.1.1.7
	netbsd-11:1.1.1.7.0.10
	netbsd-11-base:1.1.1.7
	netbsd-10-1-RELEASE:1.1.1.7
	perseant-exfatfs-base-20240630:1.1.1.7
	perseant-exfatfs:1.1.1.7.0.8
	perseant-exfatfs-base:1.1.1.7
	netbsd-8-3-RELEASE:1.1.1.6
	netbsd-9-4-RELEASE:1.1.1.6
	netbsd-10-0-RELEASE:1.1.1.7
	netbsd-10-0-RC6:1.1.1.7
	netbsd-10-0-RC5:1.1.1.7
	netbsd-10-0-RC4:1.1.1.7
	netbsd-10-0-RC3:1.1.1.7
	netbsd-10-0-RC2:1.1.1.7
	netbsd-10-0-RC1:1.1.1.7
	netbsd-10:1.1.1.7.0.6
	netbsd-10-base:1.1.1.7
	netbsd-9-3-RELEASE:1.1.1.6
	cjep_sun2x:1.1.1.7.0.4
	cjep_sun2x-base:1.1.1.7
	cjep_staticlib_x-base1:1.1.1.7
	netbsd-9-2-RELEASE:1.1.1.6
	cjep_staticlib_x:1.1.1.7.0.2
	cjep_staticlib_x-base:1.1.1.7
	netbsd-9-1-RELEASE:1.1.1.6
	phil-wifi-20200421:1.1.1.7
	phil-wifi-20200411:1.1.1.7
	phil-wifi-20200406:1.1.1.7
	netbsd-8-2-RELEASE:1.1.1.6
	netbsd-9-0-RELEASE:1.1.1.6
	netbsd-9-0-RC2:1.1.1.6
	netbsd-9-0-RC1:1.1.1.6
	netbsd-9:1.1.1.6.0.16
	netbsd-9-base:1.1.1.6
	phil-wifi-20190609:1.1.1.6
	netbsd-8-1-RELEASE:1.1.1.6
	netbsd-8-1-RC1:1.1.1.6
	pgoyette-compat-merge-20190127:1.1.1.6
	pgoyette-compat-20190127:1.1.1.6
	pgoyette-compat-20190118:1.1.1.6
	pgoyette-compat-1226:1.1.1.6
	pgoyette-compat-1126:1.1.1.6
	pgoyette-compat-1020:1.1.1.6
	pgoyette-compat-0930:1.1.1.6
	pgoyette-compat-0906:1.1.1.6
	netbsd-7-2-RELEASE:1.1.1.4
	pgoyette-compat-0728:1.1.1.6
	clang-337282:1.1.1.6
	netbsd-8-0-RELEASE:1.1.1.6
	phil-wifi:1.1.1.6.0.14
	phil-wifi-base:1.1.1.6
	pgoyette-compat-0625:1.1.1.6
	netbsd-8-0-RC2:1.1.1.6
	pgoyette-compat-0521:1.1.1.6
	pgoyette-compat-0502:1.1.1.6
	pgoyette-compat-0422:1.1.1.6
	netbsd-8-0-RC1:1.1.1.6
	pgoyette-compat-0415:1.1.1.6
	pgoyette-compat-0407:1.1.1.6
	pgoyette-compat-0330:1.1.1.6
	pgoyette-compat-0322:1.1.1.6
	pgoyette-compat-0315:1.1.1.6
	netbsd-7-1-2-RELEASE:1.1.1.4
	pgoyette-compat:1.1.1.6.0.12
	pgoyette-compat-base:1.1.1.6
	netbsd-7-1-1-RELEASE:1.1.1.4
	clang-319952:1.1.1.6
	matt-nb8-mediatek:1.1.1.6.0.10
	matt-nb8-mediatek-base:1.1.1.6
	clang-309604:1.1.1.6
	perseant-stdc-iso10646:1.1.1.6.0.8
	perseant-stdc-iso10646-base:1.1.1.6
	netbsd-8:1.1.1.6.0.6
	netbsd-8-base:1.1.1.6
	prg-localcount2-base3:1.1.1.6
	prg-localcount2-base2:1.1.1.6
	prg-localcount2-base1:1.1.1.6
	prg-localcount2:1.1.1.6.0.4
	prg-localcount2-base:1.1.1.6
	pgoyette-localcount-20170426:1.1.1.6
	bouyer-socketcan-base1:1.1.1.6
	pgoyette-localcount-20170320:1.1.1.6
	netbsd-7-1:1.1.1.4.0.10
	netbsd-7-1-RELEASE:1.1.1.4
	netbsd-7-1-RC2:1.1.1.4
	clang-294123:1.1.1.6
	netbsd-7-nhusb-base-20170116:1.1.1.4
	bouyer-socketcan:1.1.1.6.0.2
	bouyer-socketcan-base:1.1.1.6
	clang-291444:1.1.1.6
	pgoyette-localcount-20170107:1.1.1.5
	netbsd-7-1-RC1:1.1.1.4
	pgoyette-localcount-20161104:1.1.1.5
	netbsd-7-0-2-RELEASE:1.1.1.4
	localcount-20160914:1.1.1.5
	netbsd-7-nhusb:1.1.1.4.0.8
	netbsd-7-nhusb-base:1.1.1.4
	clang-280599:1.1.1.5
	pgoyette-localcount-20160806:1.1.1.5
	pgoyette-localcount-20160726:1.1.1.5
	pgoyette-localcount:1.1.1.5.0.2
	pgoyette-localcount-base:1.1.1.5
	netbsd-7-0-1-RELEASE:1.1.1.4
	clang-261930:1.1.1.5
	netbsd-7-0:1.1.1.4.0.6
	netbsd-7-0-RELEASE:1.1.1.4
	netbsd-7-0-RC3:1.1.1.4
	netbsd-7-0-RC2:1.1.1.4
	netbsd-7-0-RC1:1.1.1.4
	clang-237755:1.1.1.4
	clang-232565:1.1.1.4
	clang-227398:1.1.1.4
	tls-maxphys-base:1.1.1.4
	tls-maxphys:1.1.1.4.0.4
	netbsd-7:1.1.1.4.0.2
	netbsd-7-base:1.1.1.4
	clang-215315:1.1.1.4
	clang-209886:1.1.1.3
	yamt-pagecache:1.1.1.2.0.4
	yamt-pagecache-base9:1.1.1.2
	tls-earlyentropy:1.1.1.2.0.2
	tls-earlyentropy-base:1.1.1.3
	riastradh-xf86-video-intel-2-7-1-pre-2-21-15:1.1.1.2
	riastradh-drm2-base3:1.1.1.2
	clang-202566:1.1.1.2
	clang-201163:1.1.1.1
	clang-199312:1.1.1.1
	clang-198450:1.1.1.1
	clang-196603:1.1.1.1
	clang-195771:1.1.1.1
	LLVM:1.1.1;
locks; strict;
comment	@// @;


1.1
date	2013.11.28.14.14.52;	author joerg;	state Exp;
branches
	1.1.1.1;
next	;
commitid	ow8OybrawrB1f3fx;

1.1.1.1
date	2013.11.28.14.14.52;	author joerg;	state Exp;
branches;
next	1.1.1.2;
commitid	ow8OybrawrB1f3fx;

1.1.1.2
date	2014.03.04.19.55.00;	author joerg;	state Exp;
branches
	1.1.1.2.2.1
	1.1.1.2.4.1;
next	1.1.1.3;
commitid	29z1hJonZISIXprx;

1.1.1.3
date	2014.05.30.18.14.44;	author joerg;	state Exp;
branches;
next	1.1.1.4;
commitid	8q0kdlBlCn09GACx;

1.1.1.4
date	2014.08.10.17.08.34;	author joerg;	state Exp;
branches
	1.1.1.4.4.1;
next	1.1.1.5;
commitid	N85tXAN6Ex9VZPLx;

1.1.1.5
date	2016.02.27.22.12.06;	author joerg;	state Exp;
branches
	1.1.1.5.2.1;
next	1.1.1.6;
commitid	tIimz3oDlh1NpBWy;

1.1.1.6
date	2017.01.11.10.35.39;	author joerg;	state Exp;
branches
	1.1.1.6.14.1;
next	1.1.1.7;
commitid	CNnUNfII1jgNmxBz;

1.1.1.7
date	2019.11.13.22.19.28;	author joerg;	state dead;
branches;
next	;
commitid	QD8YATxuNG34YJKB;

1.1.1.2.2.1
date	2014.08.10.07.08.10;	author tls;	state Exp;
branches;
next	;
commitid	t01A1TLTYxkpGMLx;

1.1.1.2.4.1
date	2014.03.04.19.55.00;	author yamt;	state dead;
branches;
next	1.1.1.2.4.2;
commitid	WSrDtL5nYAUyiyBx;

1.1.1.2.4.2
date	2014.05.22.16.18.31;	author yamt;	state Exp;
branches;
next	;
commitid	WSrDtL5nYAUyiyBx;

1.1.1.4.4.1
date	2014.08.10.17.08.34;	author tls;	state dead;
branches;
next	1.1.1.4.4.2;
commitid	jTnpym9Qu0o4R1Nx;

1.1.1.4.4.2
date	2014.08.19.23.47.31;	author tls;	state Exp;
branches;
next	;
commitid	jTnpym9Qu0o4R1Nx;

1.1.1.5.2.1
date	2017.03.20.06.52.42;	author pgoyette;	state Exp;
branches;
next	;
commitid	jjw7cAwgyKq7RfKz;

1.1.1.6.14.1
date	2020.04.13.07.46.38;	author martin;	state dead;
branches;
next	;
commitid	X01YhRUPVUDaec4C;


desc
@@


1.1
log
@Initial revision
@
text
@//== NullDerefChecker.cpp - Null dereference checker ------------*- C++ -*--==//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This defines NullDerefChecker, a builtin check in ExprEngine that performs
// checks for null pointers at loads and stores.
//
//===----------------------------------------------------------------------===//

#include "ClangSACheckers.h"
#include "clang/AST/ExprObjC.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"

using namespace clang;
using namespace ento;

namespace {
class DereferenceChecker
    : public Checker< check::Location,
                      check::Bind,
                      EventDispatcher<ImplicitNullDerefEvent> > {
  mutable OwningPtr<BuiltinBug> BT_null;
  mutable OwningPtr<BuiltinBug> BT_undef;

  void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C,
                 bool IsBind = false) const;

public:
  void checkLocation(SVal location, bool isLoad, const Stmt* S,
                     CheckerContext &C) const;
  void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;

  static void AddDerefSource(raw_ostream &os,
                             SmallVectorImpl<SourceRange> &Ranges,
                             const Expr *Ex, const ProgramState *state,
                             const LocationContext *LCtx,
                             bool loadedFrom = false);
};
} // end anonymous namespace

void
DereferenceChecker::AddDerefSource(raw_ostream &os,
                                   SmallVectorImpl<SourceRange> &Ranges,
                                   const Expr *Ex,
                                   const ProgramState *state,
                                   const LocationContext *LCtx,
                                   bool loadedFrom) {
  Ex = Ex->IgnoreParenLValueCasts();
  switch (Ex->getStmtClass()) {
    default:
      break;
    case Stmt::DeclRefExprClass: {
      const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);
      if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
        os << " (" << (loadedFrom ? "loaded from" : "from")
           << " variable '" <<  VD->getName() << "')";
        Ranges.push_back(DR->getSourceRange());
      }
      break;
    }
    case Stmt::MemberExprClass: {
      const MemberExpr *ME = cast<MemberExpr>(Ex);
      os << " (" << (loadedFrom ? "loaded from" : "via")
         << " field '" << ME->getMemberNameInfo() << "')";
      SourceLocation L = ME->getMemberLoc();
      Ranges.push_back(SourceRange(L, L));
      break;
    }
    case Stmt::ObjCIvarRefExprClass: {
      const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex);
      os << " (" << (loadedFrom ? "loaded from" : "via")
         << " ivar '" << IV->getDecl()->getName() << "')";
      SourceLocation L = IV->getLocation();
      Ranges.push_back(SourceRange(L, L));
      break;
    }    
  }
}

void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S,
                                   CheckerContext &C, bool IsBind) const {
  // Generate an error node.
  ExplodedNode *N = C.generateSink(State);
  if (!N)
    return;

  // We know that 'location' cannot be non-null.  This is what
  // we call an "explicit" null dereference.
  if (!BT_null)
    BT_null.reset(new BuiltinBug("Dereference of null pointer"));

  SmallString<100> buf;
  llvm::raw_svector_ostream os(buf);

  SmallVector<SourceRange, 2> Ranges;

  // Walk through lvalue casts to get the original expression
  // that syntactically caused the load.
  if (const Expr *expr = dyn_cast<Expr>(S))
    S = expr->IgnoreParenLValueCasts();

  if (IsBind) {
    if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(S)) {
      if (BO->isAssignmentOp())
        S = BO->getRHS();
    } else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
      assert(DS->isSingleDecl() && "We process decls one by one");
      if (const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()))
        if (const Expr *Init = VD->getAnyInitializer())
          S = Init;
    }
  }

  switch (S->getStmtClass()) {
  case Stmt::ArraySubscriptExprClass: {
    os << "Array access";
    const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
    AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
                   State.getPtr(), N->getLocationContext());
    os << " results in a null pointer dereference";
    break;
  }
  case Stmt::UnaryOperatorClass: {
    os << "Dereference of null pointer";
    const UnaryOperator *U = cast<UnaryOperator>(S);
    AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
                   State.getPtr(), N->getLocationContext(), true);
    break;
  }
  case Stmt::MemberExprClass: {
    const MemberExpr *M = cast<MemberExpr>(S);
    if (M->isArrow() || bugreporter::isDeclRefExprToReference(M->getBase())) {
      os << "Access to field '" << M->getMemberNameInfo()
         << "' results in a dereference of a null pointer";
      AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
                     State.getPtr(), N->getLocationContext(), true);
    }
    break;
  }
  case Stmt::ObjCIvarRefExprClass: {
    const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
    os << "Access to instance variable '" << *IV->getDecl()
       << "' results in a dereference of a null pointer";
    AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(),
                   State.getPtr(), N->getLocationContext(), true);
    break;
  }
  default:
    break;
  }

  os.flush();
  BugReport *report =
    new BugReport(*BT_null,
                  buf.empty() ? BT_null->getDescription() : buf.str(),
                  N);

  bugreporter::trackNullOrUndefValue(N, bugreporter::getDerefExpr(S), *report);

  for (SmallVectorImpl<SourceRange>::iterator
       I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
    report->addRange(*I);

  C.emitReport(report);
}

void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
                                       CheckerContext &C) const {
  // Check for dereference of an undefined value.
  if (l.isUndef()) {
    if (ExplodedNode *N = C.generateSink()) {
      if (!BT_undef)
        BT_undef.reset(new BuiltinBug("Dereference of undefined pointer value"));

      BugReport *report =
        new BugReport(*BT_undef, BT_undef->getDescription(), N);
      bugreporter::trackNullOrUndefValue(N, bugreporter::getDerefExpr(S),
                                         *report);
      C.emitReport(report);
    }
    return;
  }

  DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>();

  // Check for null dereferences.
  if (!location.getAs<Loc>())
    return;

  ProgramStateRef state = C.getState();

  ProgramStateRef notNullState, nullState;
  llvm::tie(notNullState, nullState) = state->assume(location);

  // The explicit NULL case.
  if (nullState) {
    if (!notNullState) {
      reportBug(nullState, S, C);
      return;
    }

    // Otherwise, we have the case where the location could either be
    // null or not-null.  Record the error node as an "implicit" null
    // dereference.
    if (ExplodedNode *N = C.generateSink(nullState)) {
      ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() };
      dispatchEvent(event);
    }
  }

  // From this point forward, we know that the location is not null.
  C.addTransition(notNullState);
}

void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
                                   CheckerContext &C) const {
  // If we're binding to a reference, check if the value is known to be null.
  if (V.isUndef())
    return;

  const MemRegion *MR = L.getAsRegion();
  const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
  if (!TVR)
    return;

  if (!TVR->getValueType()->isReferenceType())
    return;

  ProgramStateRef State = C.getState();

  ProgramStateRef StNonNull, StNull;
  llvm::tie(StNonNull, StNull) =
      State->assume(V.castAs<DefinedOrUnknownSVal>());

  if (StNull) {
    if (!StNonNull) {
      reportBug(StNull, S, C, /*isBind=*/true);
      return;
    }

    // At this point the value could be either null or non-null.
    // Record this as an "implicit" null dereference.
    if (ExplodedNode *N = C.generateSink(StNull)) {
      ImplicitNullDerefEvent event = { V, /*isLoad=*/true, N,
                                       &C.getBugReporter() };
      dispatchEvent(event);
    }
  }

  // Unlike a regular null dereference, initializing a reference with a
  // dereferenced null pointer does not actually cause a runtime exception in
  // Clang's implementation of references.
  //
  //   int &r = *p; // safe??
  //   if (p != NULL) return; // uh-oh
  //   r = 5; // trap here
  //
  // The standard says this is invalid as soon as we try to create a "null
  // reference" (there is no such thing), but turning this into an assumption
  // that 'p' is never null will not match our actual runtime behavior.
  // So we do not record this assumption, allowing us to warn on the last line
  // of this example.
  //
  // We do need to add a transition because we may have generated a sink for
  // the "implicit" null dereference.
  C.addTransition(State, this);
}

void ento::registerDereferenceChecker(CheckerManager &mgr) {
  mgr.registerChecker<DereferenceChecker>();
}
@


1.1.1.1
log
@Import Clang 3.4rc1 r195771.
@
text
@@


1.1.1.2
log
@Import Clang 3.5svn r202566.
@
text
@d100 1
a100 1
    BT_null.reset(new BuiltinBug(this, "Dereference of null pointer"));
d183 1
a183 2
        BT_undef.reset(
            new BuiltinBug(this, "Dereference of undefined pointer value"));
@


1.1.1.2.2.1
log
@Rebase.
@
text
@d32 2
a33 2
  mutable std::unique_ptr<BuiltinBug> BT_null;
  mutable std::unique_ptr<BuiltinBug> BT_undef;
d204 1
a204 1
  std::tie(notNullState, nullState) = state->assume(location);
d243 2
a244 1
  std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
@


1.1.1.3
log
@Import Clang 3.5svn r209886.
@
text
@d32 2
a33 2
  mutable std::unique_ptr<BuiltinBug> BT_null;
  mutable std::unique_ptr<BuiltinBug> BT_undef;
d204 1
a204 1
  std::tie(notNullState, nullState) = state->assume(location);
d243 2
a244 1
  std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
@


1.1.1.4
log
@Import clang 3.6svn r215315.
@
text
@d129 1
a129 1
                   State.get(), N->getLocationContext());
d137 1
a137 1
                   State.get(), N->getLocationContext(), true);
d146 1
a146 1
                     State.get(), N->getLocationContext(), true);
d155 1
a155 1
                   State.get(), N->getLocationContext(), true);
@


1.1.1.5
log
@Import Clang 3.8.0rc3 r261930.
@
text
@a16 1
#include "clang/AST/ExprOpenMP.h"
a20 1
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
d35 2
a36 1
  void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C) const;
d86 1
a86 1
    }
a89 23
static const Expr *getDereferenceExpr(const Stmt *S, bool IsBind=false){
  const Expr *E = nullptr;

  // Walk through lvalue casts to get the original expression
  // that syntactically caused the load.
  if (const Expr *expr = dyn_cast<Expr>(S))
    E = expr->IgnoreParenLValueCasts();

  if (IsBind) {
    const VarDecl *VD;
    const Expr *Init;
    std::tie(VD, Init) = parseAssignment(S);
    if (VD && Init)
      E = Init;
  }
  return E;
}

static bool suppressReport(const Expr *E) {
  // Do not report dereferences on memory in non-default address spaces.
  return E->getType().getQualifiers().hasAddressSpace();
}

d91 1
a91 1
                                   CheckerContext &C) const {
d93 1
a93 1
  ExplodedNode *N = C.generateErrorNode(State);
d107 17
a132 8
  case Stmt::OMPArraySectionExprClass: {
    os << "Array access";
    const OMPArraySectionExpr *AE = cast<OMPArraySectionExpr>(S);
    AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
                   State.get(), N->getLocationContext());
    os << " results in a null pointer dereference";
    break;
  }
d162 5
a166 2
  auto report = llvm::make_unique<BugReport>(
      *BT_null, buf.empty() ? BT_null->getDescription() : StringRef(buf), N);
d174 1
a174 1
  C.emitReport(std::move(report));
d181 1
a181 1
    if (ExplodedNode *N = C.generateErrorNode()) {
d186 2
a187 2
      auto report =
          llvm::make_unique<BugReport>(*BT_undef, BT_undef->getDescription(), N);
d190 1
a190 1
      C.emitReport(std::move(report));
d209 2
a210 5
      const Expr *expr = getDereferenceExpr(S);
      if (!suppressReport(expr)) {
        reportBug(nullState, expr, C);
        return;
      }
d216 2
a217 3
    if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) {
      ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(),
                                      /*IsDirectDereference=*/false};
d247 2
a248 5
      const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true);
      if (!suppressReport(expr)) {
        reportBug(StNull, expr, C);
        return;
      }
d253 3
a255 4
    if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) {
      ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N,
                                      &C.getBugReporter(),
                                      /*IsDirectDereference=*/false};
@


1.1.1.5.2.1
log
@Sync with HEAD
@
text
@d233 1
a233 1
                                      /*IsDirectDereference=*/true};
d275 1
a275 1
                                      /*IsDirectDereference=*/true};
@


1.1.1.6
log
@Import Clang pre-4.0.0 r291444.
@
text
@d233 1
a233 1
                                      /*IsDirectDereference=*/true};
d275 1
a275 1
                                      /*IsDirectDereference=*/true};
@


1.1.1.6.14.1
log
@Mostly merge changes from HEAD upto 20200411
@
text
@@


1.1.1.7
log
@Mark old LLVM instance as dead.
@
text
@@


1.1.1.4.4.1
log
@file DereferenceChecker.cpp was added on branch tls-maxphys on 2014-08-19 23:47:31 +0000
@
text
@d1 281
@


1.1.1.4.4.2
log
@Rebase to HEAD as of a few days ago.
@
text
@a0 281
//== NullDerefChecker.cpp - Null dereference checker ------------*- C++ -*--==//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This defines NullDerefChecker, a builtin check in ExprEngine that performs
// checks for null pointers at loads and stores.
//
//===----------------------------------------------------------------------===//

#include "ClangSACheckers.h"
#include "clang/AST/ExprObjC.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"

using namespace clang;
using namespace ento;

namespace {
class DereferenceChecker
    : public Checker< check::Location,
                      check::Bind,
                      EventDispatcher<ImplicitNullDerefEvent> > {
  mutable std::unique_ptr<BuiltinBug> BT_null;
  mutable std::unique_ptr<BuiltinBug> BT_undef;

  void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C,
                 bool IsBind = false) const;

public:
  void checkLocation(SVal location, bool isLoad, const Stmt* S,
                     CheckerContext &C) const;
  void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;

  static void AddDerefSource(raw_ostream &os,
                             SmallVectorImpl<SourceRange> &Ranges,
                             const Expr *Ex, const ProgramState *state,
                             const LocationContext *LCtx,
                             bool loadedFrom = false);
};
} // end anonymous namespace

void
DereferenceChecker::AddDerefSource(raw_ostream &os,
                                   SmallVectorImpl<SourceRange> &Ranges,
                                   const Expr *Ex,
                                   const ProgramState *state,
                                   const LocationContext *LCtx,
                                   bool loadedFrom) {
  Ex = Ex->IgnoreParenLValueCasts();
  switch (Ex->getStmtClass()) {
    default:
      break;
    case Stmt::DeclRefExprClass: {
      const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);
      if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
        os << " (" << (loadedFrom ? "loaded from" : "from")
           << " variable '" <<  VD->getName() << "')";
        Ranges.push_back(DR->getSourceRange());
      }
      break;
    }
    case Stmt::MemberExprClass: {
      const MemberExpr *ME = cast<MemberExpr>(Ex);
      os << " (" << (loadedFrom ? "loaded from" : "via")
         << " field '" << ME->getMemberNameInfo() << "')";
      SourceLocation L = ME->getMemberLoc();
      Ranges.push_back(SourceRange(L, L));
      break;
    }
    case Stmt::ObjCIvarRefExprClass: {
      const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex);
      os << " (" << (loadedFrom ? "loaded from" : "via")
         << " ivar '" << IV->getDecl()->getName() << "')";
      SourceLocation L = IV->getLocation();
      Ranges.push_back(SourceRange(L, L));
      break;
    }    
  }
}

void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S,
                                   CheckerContext &C, bool IsBind) const {
  // Generate an error node.
  ExplodedNode *N = C.generateSink(State);
  if (!N)
    return;

  // We know that 'location' cannot be non-null.  This is what
  // we call an "explicit" null dereference.
  if (!BT_null)
    BT_null.reset(new BuiltinBug(this, "Dereference of null pointer"));

  SmallString<100> buf;
  llvm::raw_svector_ostream os(buf);

  SmallVector<SourceRange, 2> Ranges;

  // Walk through lvalue casts to get the original expression
  // that syntactically caused the load.
  if (const Expr *expr = dyn_cast<Expr>(S))
    S = expr->IgnoreParenLValueCasts();

  if (IsBind) {
    if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(S)) {
      if (BO->isAssignmentOp())
        S = BO->getRHS();
    } else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
      assert(DS->isSingleDecl() && "We process decls one by one");
      if (const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()))
        if (const Expr *Init = VD->getAnyInitializer())
          S = Init;
    }
  }

  switch (S->getStmtClass()) {
  case Stmt::ArraySubscriptExprClass: {
    os << "Array access";
    const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
    AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
                   State.get(), N->getLocationContext());
    os << " results in a null pointer dereference";
    break;
  }
  case Stmt::UnaryOperatorClass: {
    os << "Dereference of null pointer";
    const UnaryOperator *U = cast<UnaryOperator>(S);
    AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
                   State.get(), N->getLocationContext(), true);
    break;
  }
  case Stmt::MemberExprClass: {
    const MemberExpr *M = cast<MemberExpr>(S);
    if (M->isArrow() || bugreporter::isDeclRefExprToReference(M->getBase())) {
      os << "Access to field '" << M->getMemberNameInfo()
         << "' results in a dereference of a null pointer";
      AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
                     State.get(), N->getLocationContext(), true);
    }
    break;
  }
  case Stmt::ObjCIvarRefExprClass: {
    const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
    os << "Access to instance variable '" << *IV->getDecl()
       << "' results in a dereference of a null pointer";
    AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(),
                   State.get(), N->getLocationContext(), true);
    break;
  }
  default:
    break;
  }

  os.flush();
  BugReport *report =
    new BugReport(*BT_null,
                  buf.empty() ? BT_null->getDescription() : buf.str(),
                  N);

  bugreporter::trackNullOrUndefValue(N, bugreporter::getDerefExpr(S), *report);

  for (SmallVectorImpl<SourceRange>::iterator
       I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
    report->addRange(*I);

  C.emitReport(report);
}

void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
                                       CheckerContext &C) const {
  // Check for dereference of an undefined value.
  if (l.isUndef()) {
    if (ExplodedNode *N = C.generateSink()) {
      if (!BT_undef)
        BT_undef.reset(
            new BuiltinBug(this, "Dereference of undefined pointer value"));

      BugReport *report =
        new BugReport(*BT_undef, BT_undef->getDescription(), N);
      bugreporter::trackNullOrUndefValue(N, bugreporter::getDerefExpr(S),
                                         *report);
      C.emitReport(report);
    }
    return;
  }

  DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>();

  // Check for null dereferences.
  if (!location.getAs<Loc>())
    return;

  ProgramStateRef state = C.getState();

  ProgramStateRef notNullState, nullState;
  std::tie(notNullState, nullState) = state->assume(location);

  // The explicit NULL case.
  if (nullState) {
    if (!notNullState) {
      reportBug(nullState, S, C);
      return;
    }

    // Otherwise, we have the case where the location could either be
    // null or not-null.  Record the error node as an "implicit" null
    // dereference.
    if (ExplodedNode *N = C.generateSink(nullState)) {
      ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() };
      dispatchEvent(event);
    }
  }

  // From this point forward, we know that the location is not null.
  C.addTransition(notNullState);
}

void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
                                   CheckerContext &C) const {
  // If we're binding to a reference, check if the value is known to be null.
  if (V.isUndef())
    return;

  const MemRegion *MR = L.getAsRegion();
  const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
  if (!TVR)
    return;

  if (!TVR->getValueType()->isReferenceType())
    return;

  ProgramStateRef State = C.getState();

  ProgramStateRef StNonNull, StNull;
  std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());

  if (StNull) {
    if (!StNonNull) {
      reportBug(StNull, S, C, /*isBind=*/true);
      return;
    }

    // At this point the value could be either null or non-null.
    // Record this as an "implicit" null dereference.
    if (ExplodedNode *N = C.generateSink(StNull)) {
      ImplicitNullDerefEvent event = { V, /*isLoad=*/true, N,
                                       &C.getBugReporter() };
      dispatchEvent(event);
    }
  }

  // Unlike a regular null dereference, initializing a reference with a
  // dereferenced null pointer does not actually cause a runtime exception in
  // Clang's implementation of references.
  //
  //   int &r = *p; // safe??
  //   if (p != NULL) return; // uh-oh
  //   r = 5; // trap here
  //
  // The standard says this is invalid as soon as we try to create a "null
  // reference" (there is no such thing), but turning this into an assumption
  // that 'p' is never null will not match our actual runtime behavior.
  // So we do not record this assumption, allowing us to warn on the last line
  // of this example.
  //
  // We do need to add a transition because we may have generated a sink for
  // the "implicit" null dereference.
  C.addTransition(State, this);
}

void ento::registerDereferenceChecker(CheckerManager &mgr) {
  mgr.registerChecker<DereferenceChecker>();
}
@


1.1.1.2.4.1
log
@file DereferenceChecker.cpp was added on branch yamt-pagecache on 2014-05-22 16:18:31 +0000
@
text
@d1 282
@


1.1.1.2.4.2
log
@sync with head.

for a reference, the tree before this commit was tagged
as yamt-pagecache-tag8.

this commit was splitted into small chunks to avoid
a limitation of cvs.  ("Protocol error: too many arguments")
@
text
@a0 282
//== NullDerefChecker.cpp - Null dereference checker ------------*- C++ -*--==//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This defines NullDerefChecker, a builtin check in ExprEngine that performs
// checks for null pointers at loads and stores.
//
//===----------------------------------------------------------------------===//

#include "ClangSACheckers.h"
#include "clang/AST/ExprObjC.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"

using namespace clang;
using namespace ento;

namespace {
class DereferenceChecker
    : public Checker< check::Location,
                      check::Bind,
                      EventDispatcher<ImplicitNullDerefEvent> > {
  mutable OwningPtr<BuiltinBug> BT_null;
  mutable OwningPtr<BuiltinBug> BT_undef;

  void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C,
                 bool IsBind = false) const;

public:
  void checkLocation(SVal location, bool isLoad, const Stmt* S,
                     CheckerContext &C) const;
  void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;

  static void AddDerefSource(raw_ostream &os,
                             SmallVectorImpl<SourceRange> &Ranges,
                             const Expr *Ex, const ProgramState *state,
                             const LocationContext *LCtx,
                             bool loadedFrom = false);
};
} // end anonymous namespace

void
DereferenceChecker::AddDerefSource(raw_ostream &os,
                                   SmallVectorImpl<SourceRange> &Ranges,
                                   const Expr *Ex,
                                   const ProgramState *state,
                                   const LocationContext *LCtx,
                                   bool loadedFrom) {
  Ex = Ex->IgnoreParenLValueCasts();
  switch (Ex->getStmtClass()) {
    default:
      break;
    case Stmt::DeclRefExprClass: {
      const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);
      if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
        os << " (" << (loadedFrom ? "loaded from" : "from")
           << " variable '" <<  VD->getName() << "')";
        Ranges.push_back(DR->getSourceRange());
      }
      break;
    }
    case Stmt::MemberExprClass: {
      const MemberExpr *ME = cast<MemberExpr>(Ex);
      os << " (" << (loadedFrom ? "loaded from" : "via")
         << " field '" << ME->getMemberNameInfo() << "')";
      SourceLocation L = ME->getMemberLoc();
      Ranges.push_back(SourceRange(L, L));
      break;
    }
    case Stmt::ObjCIvarRefExprClass: {
      const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex);
      os << " (" << (loadedFrom ? "loaded from" : "via")
         << " ivar '" << IV->getDecl()->getName() << "')";
      SourceLocation L = IV->getLocation();
      Ranges.push_back(SourceRange(L, L));
      break;
    }    
  }
}

void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S,
                                   CheckerContext &C, bool IsBind) const {
  // Generate an error node.
  ExplodedNode *N = C.generateSink(State);
  if (!N)
    return;

  // We know that 'location' cannot be non-null.  This is what
  // we call an "explicit" null dereference.
  if (!BT_null)
    BT_null.reset(new BuiltinBug(this, "Dereference of null pointer"));

  SmallString<100> buf;
  llvm::raw_svector_ostream os(buf);

  SmallVector<SourceRange, 2> Ranges;

  // Walk through lvalue casts to get the original expression
  // that syntactically caused the load.
  if (const Expr *expr = dyn_cast<Expr>(S))
    S = expr->IgnoreParenLValueCasts();

  if (IsBind) {
    if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(S)) {
      if (BO->isAssignmentOp())
        S = BO->getRHS();
    } else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
      assert(DS->isSingleDecl() && "We process decls one by one");
      if (const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()))
        if (const Expr *Init = VD->getAnyInitializer())
          S = Init;
    }
  }

  switch (S->getStmtClass()) {
  case Stmt::ArraySubscriptExprClass: {
    os << "Array access";
    const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
    AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
                   State.getPtr(), N->getLocationContext());
    os << " results in a null pointer dereference";
    break;
  }
  case Stmt::UnaryOperatorClass: {
    os << "Dereference of null pointer";
    const UnaryOperator *U = cast<UnaryOperator>(S);
    AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
                   State.getPtr(), N->getLocationContext(), true);
    break;
  }
  case Stmt::MemberExprClass: {
    const MemberExpr *M = cast<MemberExpr>(S);
    if (M->isArrow() || bugreporter::isDeclRefExprToReference(M->getBase())) {
      os << "Access to field '" << M->getMemberNameInfo()
         << "' results in a dereference of a null pointer";
      AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
                     State.getPtr(), N->getLocationContext(), true);
    }
    break;
  }
  case Stmt::ObjCIvarRefExprClass: {
    const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
    os << "Access to instance variable '" << *IV->getDecl()
       << "' results in a dereference of a null pointer";
    AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(),
                   State.getPtr(), N->getLocationContext(), true);
    break;
  }
  default:
    break;
  }

  os.flush();
  BugReport *report =
    new BugReport(*BT_null,
                  buf.empty() ? BT_null->getDescription() : buf.str(),
                  N);

  bugreporter::trackNullOrUndefValue(N, bugreporter::getDerefExpr(S), *report);

  for (SmallVectorImpl<SourceRange>::iterator
       I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
    report->addRange(*I);

  C.emitReport(report);
}

void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
                                       CheckerContext &C) const {
  // Check for dereference of an undefined value.
  if (l.isUndef()) {
    if (ExplodedNode *N = C.generateSink()) {
      if (!BT_undef)
        BT_undef.reset(
            new BuiltinBug(this, "Dereference of undefined pointer value"));

      BugReport *report =
        new BugReport(*BT_undef, BT_undef->getDescription(), N);
      bugreporter::trackNullOrUndefValue(N, bugreporter::getDerefExpr(S),
                                         *report);
      C.emitReport(report);
    }
    return;
  }

  DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>();

  // Check for null dereferences.
  if (!location.getAs<Loc>())
    return;

  ProgramStateRef state = C.getState();

  ProgramStateRef notNullState, nullState;
  llvm::tie(notNullState, nullState) = state->assume(location);

  // The explicit NULL case.
  if (nullState) {
    if (!notNullState) {
      reportBug(nullState, S, C);
      return;
    }

    // Otherwise, we have the case where the location could either be
    // null or not-null.  Record the error node as an "implicit" null
    // dereference.
    if (ExplodedNode *N = C.generateSink(nullState)) {
      ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() };
      dispatchEvent(event);
    }
  }

  // From this point forward, we know that the location is not null.
  C.addTransition(notNullState);
}

void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
                                   CheckerContext &C) const {
  // If we're binding to a reference, check if the value is known to be null.
  if (V.isUndef())
    return;

  const MemRegion *MR = L.getAsRegion();
  const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
  if (!TVR)
    return;

  if (!TVR->getValueType()->isReferenceType())
    return;

  ProgramStateRef State = C.getState();

  ProgramStateRef StNonNull, StNull;
  llvm::tie(StNonNull, StNull) =
      State->assume(V.castAs<DefinedOrUnknownSVal>());

  if (StNull) {
    if (!StNonNull) {
      reportBug(StNull, S, C, /*isBind=*/true);
      return;
    }

    // At this point the value could be either null or non-null.
    // Record this as an "implicit" null dereference.
    if (ExplodedNode *N = C.generateSink(StNull)) {
      ImplicitNullDerefEvent event = { V, /*isLoad=*/true, N,
                                       &C.getBugReporter() };
      dispatchEvent(event);
    }
  }

  // Unlike a regular null dereference, initializing a reference with a
  // dereferenced null pointer does not actually cause a runtime exception in
  // Clang's implementation of references.
  //
  //   int &r = *p; // safe??
  //   if (p != NULL) return; // uh-oh
  //   r = 5; // trap here
  //
  // The standard says this is invalid as soon as we try to create a "null
  // reference" (there is no such thing), but turning this into an assumption
  // that 'p' is never null will not match our actual runtime behavior.
  // So we do not record this assumption, allowing us to warn on the last line
  // of this example.
  //
  // We do need to add a transition because we may have generated a sink for
  // the "implicit" null dereference.
  C.addTransition(State, this);
}

void ento::registerDereferenceChecker(CheckerManager &mgr) {
  mgr.registerChecker<DereferenceChecker>();
}
@


