forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			310 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			310 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
//===-- DereferenceChecker.cpp - Null dereference checker -----------------===//
 | 
						|
//
 | 
						|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | 
						|
// See https://llvm.org/LICENSE.txt for license information.
 | 
						|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
//
 | 
						|
// This defines NullDerefChecker, a builtin check in ExprEngine that performs
 | 
						|
// checks for null pointers at loads and stores.
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
 | 
						|
#include "clang/AST/ExprObjC.h"
 | 
						|
#include "clang/AST/ExprOpenMP.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 "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.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) 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;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
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().hasAddressSpace();
 | 
						|
}
 | 
						|
 | 
						|
static bool isDeclRefExprToReference(const Expr *E) {
 | 
						|
  if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
 | 
						|
    return DRE->getDecl()->getType()->isReferenceType();
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S,
 | 
						|
                                   CheckerContext &C) const {
 | 
						|
  // Generate an error node.
 | 
						|
  ExplodedNode *N = C.generateErrorNode(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;
 | 
						|
 | 
						|
  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::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;
 | 
						|
  }
 | 
						|
  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() || 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;
 | 
						|
  }
 | 
						|
 | 
						|
  auto report = std::make_unique<PathSensitiveBugReport>(
 | 
						|
      *BT_null, buf.empty() ? BT_null->getDescription() : StringRef(buf), N);
 | 
						|
 | 
						|
  bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report);
 | 
						|
 | 
						|
  for (SmallVectorImpl<SourceRange>::iterator
 | 
						|
       I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
 | 
						|
    report->addRange(*I);
 | 
						|
 | 
						|
  C.emitReport(std::move(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.generateErrorNode()) {
 | 
						|
      if (!BT_undef)
 | 
						|
        BT_undef.reset(
 | 
						|
            new BuiltinBug(this, "Dereference of undefined pointer value"));
 | 
						|
 | 
						|
      auto report = std::make_unique<PathSensitiveBugReport>(
 | 
						|
          *BT_undef, BT_undef->getDescription(), N);
 | 
						|
      bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report);
 | 
						|
      C.emitReport(std::move(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) {
 | 
						|
      const Expr *expr = getDereferenceExpr(S);
 | 
						|
      if (!suppressReport(expr)) {
 | 
						|
        reportBug(nullState, expr, 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, C.getPredecessor())) {
 | 
						|
      ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(),
 | 
						|
                                      /*IsDirectDereference=*/true};
 | 
						|
      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) {
 | 
						|
      const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true);
 | 
						|
      if (!suppressReport(expr)) {
 | 
						|
        reportBug(StNull, expr, C);
 | 
						|
        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, C.getPredecessor())) {
 | 
						|
      ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N,
 | 
						|
                                      &C.getBugReporter(),
 | 
						|
                                      /*IsDirectDereference=*/true};
 | 
						|
      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>();
 | 
						|
}
 | 
						|
 | 
						|
bool ento::shouldRegisterDereferenceChecker(const LangOptions &LO) {
 | 
						|
  return true;
 | 
						|
}
 |