NonNullParamChecker.cpp revision 360660
133965Sjdp//===--- NonNullParamChecker.cpp - Undefined arguments checker -*- C++ -*--===//
2218822Sdim//
3218822Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
433965Sjdp// See https://llvm.org/LICENSE.txt for license information.
533965Sjdp// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6130561Sobrien//
733965Sjdp//===----------------------------------------------------------------------===//
8130561Sobrien//
9130561Sobrien// This defines NonNullParamChecker, which checks for arguments expected not to
10130561Sobrien// be null due to:
11130561Sobrien//   - the corresponding parameters being declared to have nonnull attribute
1233965Sjdp//   - the corresponding parameters being references; since the call would form
13130561Sobrien//     a reference to a null pointer
14130561Sobrien//
15130561Sobrien//===----------------------------------------------------------------------===//
16130561Sobrien
1733965Sjdp#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
18130561Sobrien#include "clang/AST/Attr.h"
19130561Sobrien#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
20218822Sdim#include "clang/StaticAnalyzer/Core/Checker.h"
2133965Sjdp#include "clang/StaticAnalyzer/Core/CheckerManager.h"
2233965Sjdp#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
2333965Sjdp#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
2433965Sjdp
2533965Sjdpusing namespace clang;
2633965Sjdpusing namespace ento;
2733965Sjdp
2833965Sjdpnamespace {
2933965Sjdpclass NonNullParamChecker
3033965Sjdp  : public Checker< check::PreCall, EventDispatcher<ImplicitNullDerefEvent> > {
3133965Sjdp  mutable std::unique_ptr<BugType> BTAttrNonNull;
3233965Sjdp  mutable std::unique_ptr<BugType> BTNullRefArg;
3333965Sjdp
3433965Sjdppublic:
35218822Sdim
3633965Sjdp  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
3789857Sobrien
3833965Sjdp  std::unique_ptr<BugReport>
3933965Sjdp  genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE) const;
4033965Sjdp  std::unique_ptr<BugReport>
4133965Sjdp  genReportReferenceToNullPointer(const ExplodedNode *ErrorN,
4233965Sjdp                                  const Expr *ArgE) const;
4333965Sjdp};
44130561Sobrien} // end anonymous namespace
45130561Sobrien
46130561Sobrien/// \return Bitvector marking non-null attributes.
47130561Sobrienstatic llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) {
4889857Sobrien  const Decl *FD = Call.getDecl();
4933965Sjdp  unsigned NumArgs = Call.getNumArgs();
5033965Sjdp  llvm::SmallBitVector AttrNonNull(NumArgs);
51130561Sobrien  for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) {
52218822Sdim    if (!NonNull->args_size()) {
5333965Sjdp      AttrNonNull.set(0, NumArgs);
54130561Sobrien      break;
5533965Sjdp    }
5633965Sjdp    for (const ParamIdx &Idx : NonNull->args()) {
5733965Sjdp      unsigned IdxAST = Idx.getASTIndex();
5833965Sjdp      if (IdxAST >= NumArgs)
5933965Sjdp        continue;
6033965Sjdp      AttrNonNull.set(IdxAST);
6133965Sjdp    }
62218822Sdim  }
6333965Sjdp  return AttrNonNull;
6433965Sjdp}
6533965Sjdp
66218822Sdimvoid NonNullParamChecker::checkPreCall(const CallEvent &Call,
6733965Sjdp                                       CheckerContext &C) const {
6833965Sjdp  if (!Call.getDecl())
6933965Sjdp    return;
7033965Sjdp
7133965Sjdp  llvm::SmallBitVector AttrNonNull = getNonNullAttrs(Call);
7233965Sjdp  unsigned NumArgs = Call.getNumArgs();
7333965Sjdp
7433965Sjdp  ProgramStateRef state = C.getState();
7533965Sjdp  ArrayRef<ParmVarDecl*> parms = Call.parameters();
7633965Sjdp
7733965Sjdp  for (unsigned idx = 0; idx < NumArgs; ++idx) {
7833965Sjdp    // For vararg functions, a corresponding parameter decl may not exist.
7933965Sjdp    bool HasParam = idx < parms.size();
8033965Sjdp
8133965Sjdp    // Check if the parameter is a reference. We want to report when reference
8233965Sjdp    // to a null pointer is passed as a parameter.
8333965Sjdp    bool haveRefTypeParam =
84218822Sdim        HasParam ? parms[idx]->getType()->isReferenceType() : false;
85218822Sdim    bool haveAttrNonNull = AttrNonNull[idx];
8633965Sjdp
8733965Sjdp    // Check if the parameter is also marked 'nonnull'.
8833965Sjdp    if (!haveAttrNonNull && HasParam)
89218822Sdim      haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>();
9033965Sjdp
9133965Sjdp    if (!haveAttrNonNull && !haveRefTypeParam)
92218822Sdim      continue;
9333965Sjdp
9489857Sobrien    // If the value is unknown or undefined, we can't perform this check.
9589857Sobrien    const Expr *ArgE = Call.getArgExpr(idx);
9689857Sobrien    SVal V = Call.getArgSVal(idx);
9789857Sobrien    auto DV = V.getAs<DefinedSVal>();
98130561Sobrien    if (!DV)
99130561Sobrien      continue;
10089857Sobrien
10189857Sobrien    assert(!haveRefTypeParam || DV->getAs<Loc>());
10233965Sjdp
10333965Sjdp    // Process the case when the argument is not a location.
10433965Sjdp    if (haveAttrNonNull && !DV->getAs<Loc>()) {
105218822Sdim      // If the argument is a union type, we want to handle a potential
106218822Sdim      // transparent_union GCC extension.
107218822Sdim      if (!ArgE)
10833965Sjdp        continue;
10933965Sjdp
11033965Sjdp      QualType T = ArgE->getType();
111130561Sobrien      const RecordType *UT = T->getAsUnionType();
112218822Sdim      if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>())
113218822Sdim        continue;
114218822Sdim
115218822Sdim      auto CSV = DV->getAs<nonloc::CompoundVal>();
116218822Sdim
11733965Sjdp      // FIXME: Handle LazyCompoundVals?
11833965Sjdp      if (!CSV)
11989857Sobrien        continue;
120130561Sobrien
121130561Sobrien      V = *(CSV->begin());
12233965Sjdp      DV = V.getAs<DefinedSVal>();
12333965Sjdp      assert(++CSV->begin() == CSV->end());
12433965Sjdp      // FIXME: Handle (some_union){ some_other_union_val }, which turns into
12533965Sjdp      // a LazyCompoundVal inside a CompoundVal.
12633965Sjdp      if (!V.getAs<Loc>())
127218822Sdim        continue;
12833965Sjdp
12933965Sjdp      // Retrieve the corresponding expression.
13033965Sjdp      if (const auto *CE = dyn_cast<CompoundLiteralExpr>(ArgE))
13133965Sjdp        if (const auto *IE = dyn_cast<InitListExpr>(CE->getInitializer()))
13233965Sjdp          ArgE = dyn_cast<Expr>(*(IE->begin()));
13333965Sjdp    }
13433965Sjdp
135218822Sdim    ConstraintManager &CM = C.getConstraintManager();
13633965Sjdp    ProgramStateRef stateNotNull, stateNull;
13789857Sobrien    std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
13833965Sjdp
13933965Sjdp    // Generate an error node.  Check for a null node in case
14033965Sjdp    // we cache out.
14133965Sjdp    if (stateNull && !stateNotNull) {
14233965Sjdp      if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) {
14333965Sjdp
14433965Sjdp        std::unique_ptr<BugReport> R;
145218822Sdim        if (haveAttrNonNull)
14633965Sjdp          R = genReportNullAttrNonNull(errorNode, ArgE);
14733965Sjdp        else if (haveRefTypeParam)
14833965Sjdp          R = genReportReferenceToNullPointer(errorNode, ArgE);
14933965Sjdp
15033965Sjdp        // Highlight the range of the argument that was null.
15133965Sjdp        R->addRange(Call.getArgSourceRange(idx));
15233965Sjdp
15389857Sobrien        // Emit the bug report.
15433965Sjdp        C.emitReport(std::move(R));
15533965Sjdp      }
15633965Sjdp
15733965Sjdp      // Always return.  Either we cached out or we just emitted an error.
15833965Sjdp      return;
15933965Sjdp    }
16033965Sjdp
16133965Sjdp    if (stateNull) {
162218822Sdim      if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) {
16333965Sjdp        ImplicitNullDerefEvent event = {
16433965Sjdp          V, false, N, &C.getBugReporter(),
16533965Sjdp          /*IsDirectDereference=*/haveRefTypeParam};
16633965Sjdp        dispatchEvent(event);
16789857Sobrien      }
16833965Sjdp    }
169218822Sdim
17033965Sjdp    // If a pointer value passed the check we should assume that it is
171130561Sobrien    // indeed not null from this point forward.
17233965Sjdp    state = stateNotNull;
17333965Sjdp  }
17433965Sjdp
17533965Sjdp  // If we reach here all of the arguments passed the nonnull check.
17633965Sjdp  // If 'state' has been updated generated a new node.
17733965Sjdp  C.addTransition(state);
17833965Sjdp}
17933965Sjdp
18033965Sjdpstd::unique_ptr<BugReport>
18133965SjdpNonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode,
18233965Sjdp                                              const Expr *ArgE) const {
18333965Sjdp  // Lazily allocate the BugType object if it hasn't already been
184218822Sdim  // created. Ownership is transferred to the BugReporter object once
18533965Sjdp  // the BugReport is passed to 'EmitWarning'.
18633965Sjdp  if (!BTAttrNonNull)
18733965Sjdp    BTAttrNonNull.reset(new BugType(
18833965Sjdp        this, "Argument with 'nonnull' attribute passed null", "API"));
18933965Sjdp
19033965Sjdp  auto R = llvm::make_unique<BugReport>(
19133965Sjdp      *BTAttrNonNull,
192218822Sdim      "Null pointer passed as an argument to a 'nonnull' parameter", ErrorNode);
19333965Sjdp  if (ArgE)
19433965Sjdp    bugreporter::trackExpressionValue(ErrorNode, ArgE, *R);
19533965Sjdp
19633965Sjdp  return R;
19733965Sjdp}
19833965Sjdp
19933965Sjdpstd::unique_ptr<BugReport> NonNullParamChecker::genReportReferenceToNullPointer(
20033965Sjdp    const ExplodedNode *ErrorNode, const Expr *ArgE) const {
20133965Sjdp  if (!BTNullRefArg)
20233965Sjdp    BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer"));
20333965Sjdp
204218822Sdim  auto R = llvm::make_unique<BugReport>(
205218822Sdim      *BTNullRefArg, "Forming reference to null pointer", ErrorNode);
20633965Sjdp  if (ArgE) {
20733965Sjdp    const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE);
20833965Sjdp    if (!ArgEDeref)
20933965Sjdp      ArgEDeref = ArgE;
210218822Sdim    bugreporter::trackExpressionValue(ErrorNode, ArgEDeref, *R);
211218822Sdim  }
212218822Sdim  return R;
21333965Sjdp
21433965Sjdp}
21533965Sjdp
21633965Sjdpvoid ento::registerNonNullParamChecker(CheckerManager &mgr) {
217218822Sdim  mgr.registerChecker<NonNullParamChecker>();
218218822Sdim}
219218822Sdim
220218822Sdimbool ento::shouldRegisterNonNullParamChecker(const LangOptions &LO) {
221218822Sdim  return true;
222218822Sdim}
223218822Sdim