1249261Sdim//===--- NonNullParamChecker.cpp - Undefined arguments checker -*- C++ -*--===//
2249261Sdim//
3249261Sdim//                     The LLVM Compiler Infrastructure
4249261Sdim//
5249261Sdim// This file is distributed under the University of Illinois Open Source
6249261Sdim// License. See LICENSE.TXT for details.
7249261Sdim//
8249261Sdim//===----------------------------------------------------------------------===//
9249261Sdim//
10249261Sdim// This defines NonNullParamChecker, which checks for arguments expected not to
11249261Sdim// be null due to:
12249261Sdim//   - the corresponding parameters being declared to have nonnull attribute
13249261Sdim//   - the corresponding parameters being references; since the call would form
14249261Sdim//     a reference to a null pointer
15249261Sdim//
16249261Sdim//===----------------------------------------------------------------------===//
17249261Sdim
18249261Sdim#include "ClangSACheckers.h"
19249261Sdim#include "clang/AST/Attr.h"
20249261Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
21249261Sdim#include "clang/StaticAnalyzer/Core/Checker.h"
22249261Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h"
23249261Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
24249261Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
25249261Sdim
26249261Sdimusing namespace clang;
27249261Sdimusing namespace ento;
28249261Sdim
29249261Sdimnamespace {
30249261Sdimclass NonNullParamChecker
31296417Sdim  : public Checker< check::PreCall, EventDispatcher<ImplicitNullDerefEvent> > {
32276479Sdim  mutable std::unique_ptr<BugType> BTAttrNonNull;
33276479Sdim  mutable std::unique_ptr<BugType> BTNullRefArg;
34276479Sdim
35249261Sdimpublic:
36249261Sdim
37249261Sdim  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
38249261Sdim
39288943Sdim  std::unique_ptr<BugReport>
40288943Sdim  genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE) const;
41288943Sdim  std::unique_ptr<BugReport>
42288943Sdim  genReportReferenceToNullPointer(const ExplodedNode *ErrorN,
43288943Sdim                                  const Expr *ArgE) const;
44249261Sdim};
45249261Sdim} // end anonymous namespace
46249261Sdim
47249261Sdimvoid NonNullParamChecker::checkPreCall(const CallEvent &Call,
48276479Sdim                                       CheckerContext &C) const {
49249261Sdim  const Decl *FD = Call.getDecl();
50249261Sdim  if (!FD)
51249261Sdim    return;
52249261Sdim
53280031Sdim  // Merge all non-null attributes
54280031Sdim  unsigned NumArgs = Call.getNumArgs();
55280031Sdim  llvm::SmallBitVector AttrNonNull(NumArgs);
56280031Sdim  for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) {
57280031Sdim    if (!NonNull->args_size()) {
58280031Sdim      AttrNonNull.set(0, NumArgs);
59280031Sdim      break;
60280031Sdim    }
61280031Sdim    for (unsigned Val : NonNull->args()) {
62280031Sdim      if (Val >= NumArgs)
63280031Sdim        continue;
64280031Sdim      AttrNonNull.set(Val);
65280031Sdim    }
66280031Sdim  }
67249261Sdim
68249261Sdim  ProgramStateRef state = C.getState();
69249261Sdim
70249261Sdim  CallEvent::param_type_iterator TyI = Call.param_type_begin(),
71249261Sdim                                 TyE = Call.param_type_end();
72249261Sdim
73280031Sdim  for (unsigned idx = 0; idx < NumArgs; ++idx) {
74249261Sdim
75249261Sdim    // Check if the parameter is a reference. We want to report when reference
76249261Sdim    // to a null pointer is passed as a paramter.
77249261Sdim    bool haveRefTypeParam = false;
78249261Sdim    if (TyI != TyE) {
79249261Sdim      haveRefTypeParam = (*TyI)->isReferenceType();
80249261Sdim      TyI++;
81249261Sdim    }
82249261Sdim
83280031Sdim    bool haveAttrNonNull = AttrNonNull[idx];
84276479Sdim    if (!haveAttrNonNull) {
85276479Sdim      // Check if the parameter is also marked 'nonnull'.
86276479Sdim      ArrayRef<ParmVarDecl*> parms = Call.parameters();
87276479Sdim      if (idx < parms.size())
88276479Sdim        haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>();
89276479Sdim    }
90249261Sdim
91249261Sdim    if (!haveRefTypeParam && !haveAttrNonNull)
92249261Sdim      continue;
93249261Sdim
94249261Sdim    // If the value is unknown or undefined, we can't perform this check.
95249261Sdim    const Expr *ArgE = Call.getArgExpr(idx);
96249261Sdim    SVal V = Call.getArgSVal(idx);
97249261Sdim    Optional<DefinedSVal> DV = V.getAs<DefinedSVal>();
98249261Sdim    if (!DV)
99249261Sdim      continue;
100249261Sdim
101249261Sdim    // Process the case when the argument is not a location.
102249261Sdim    assert(!haveRefTypeParam || DV->getAs<Loc>());
103249261Sdim
104249261Sdim    if (haveAttrNonNull && !DV->getAs<Loc>()) {
105249261Sdim      // If the argument is a union type, we want to handle a potential
106249261Sdim      // transparent_union GCC extension.
107249261Sdim      if (!ArgE)
108249261Sdim        continue;
109249261Sdim
110249261Sdim      QualType T = ArgE->getType();
111249261Sdim      const RecordType *UT = T->getAsUnionType();
112249261Sdim      if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>())
113249261Sdim        continue;
114249261Sdim
115249261Sdim      if (Optional<nonloc::CompoundVal> CSV =
116249261Sdim              DV->getAs<nonloc::CompoundVal>()) {
117249261Sdim        nonloc::CompoundVal::iterator CSV_I = CSV->begin();
118249261Sdim        assert(CSV_I != CSV->end());
119249261Sdim        V = *CSV_I;
120249261Sdim        DV = V.getAs<DefinedSVal>();
121249261Sdim        assert(++CSV_I == CSV->end());
122276479Sdim        // FIXME: Handle (some_union){ some_other_union_val }, which turns into
123276479Sdim        // a LazyCompoundVal inside a CompoundVal.
124276479Sdim        if (!V.getAs<Loc>())
125249261Sdim          continue;
126249261Sdim        // Retrieve the corresponding expression.
127249261Sdim        if (const CompoundLiteralExpr *CE = dyn_cast<CompoundLiteralExpr>(ArgE))
128249261Sdim          if (const InitListExpr *IE =
129249261Sdim                dyn_cast<InitListExpr>(CE->getInitializer()))
130249261Sdim             ArgE = dyn_cast<Expr>(*(IE->begin()));
131249261Sdim
132249261Sdim      } else {
133249261Sdim        // FIXME: Handle LazyCompoundVals?
134249261Sdim        continue;
135249261Sdim      }
136249261Sdim    }
137249261Sdim
138249261Sdim    ConstraintManager &CM = C.getConstraintManager();
139249261Sdim    ProgramStateRef stateNotNull, stateNull;
140276479Sdim    std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
141249261Sdim
142296417Sdim    if (stateNull) {
143296417Sdim      if (!stateNotNull) {
144296417Sdim        // Generate an error node.  Check for a null node in case
145296417Sdim        // we cache out.
146296417Sdim        if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) {
147249261Sdim
148296417Sdim          std::unique_ptr<BugReport> R;
149296417Sdim          if (haveAttrNonNull)
150296417Sdim            R = genReportNullAttrNonNull(errorNode, ArgE);
151296417Sdim          else if (haveRefTypeParam)
152296417Sdim            R = genReportReferenceToNullPointer(errorNode, ArgE);
153249261Sdim
154296417Sdim          // Highlight the range of the argument that was null.
155296417Sdim          R->addRange(Call.getArgSourceRange(idx));
156249261Sdim
157296417Sdim          // Emit the bug report.
158296417Sdim          C.emitReport(std::move(R));
159296417Sdim        }
160296417Sdim
161296417Sdim        // Always return.  Either we cached out or we just emitted an error.
162296417Sdim        return;
163249261Sdim      }
164296417Sdim      if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) {
165296417Sdim        ImplicitNullDerefEvent event = {
166296417Sdim            V, false, N, &C.getBugReporter(),
167296417Sdim            /*IsDirectDereference=*/haveRefTypeParam};
168296417Sdim        dispatchEvent(event);
169296417Sdim      }
170249261Sdim    }
171249261Sdim
172249261Sdim    // If a pointer value passed the check we should assume that it is
173249261Sdim    // indeed not null from this point forward.
174249261Sdim    assert(stateNotNull);
175249261Sdim    state = stateNotNull;
176249261Sdim  }
177249261Sdim
178249261Sdim  // If we reach here all of the arguments passed the nonnull check.
179249261Sdim  // If 'state' has been updated generated a new node.
180249261Sdim  C.addTransition(state);
181249261Sdim}
182249261Sdim
183288943Sdimstd::unique_ptr<BugReport>
184288943SdimNonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode,
185288943Sdim                                              const Expr *ArgE) const {
186249261Sdim  // Lazily allocate the BugType object if it hasn't already been
187249261Sdim  // created. Ownership is transferred to the BugReporter object once
188249261Sdim  // the BugReport is passed to 'EmitWarning'.
189249261Sdim  if (!BTAttrNonNull)
190249261Sdim    BTAttrNonNull.reset(new BugType(
191276479Sdim        this, "Argument with 'nonnull' attribute passed null", "API"));
192249261Sdim
193288943Sdim  auto R = llvm::make_unique<BugReport>(
194288943Sdim      *BTAttrNonNull,
195288943Sdim      "Null pointer passed as an argument to a 'nonnull' parameter", ErrorNode);
196249261Sdim  if (ArgE)
197249261Sdim    bugreporter::trackNullOrUndefValue(ErrorNode, ArgE, *R);
198249261Sdim
199249261Sdim  return R;
200249261Sdim}
201249261Sdim
202288943Sdimstd::unique_ptr<BugReport> NonNullParamChecker::genReportReferenceToNullPointer(
203288943Sdim    const ExplodedNode *ErrorNode, const Expr *ArgE) const {
204249261Sdim  if (!BTNullRefArg)
205276479Sdim    BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer"));
206249261Sdim
207288943Sdim  auto R = llvm::make_unique<BugReport>(
208288943Sdim      *BTNullRefArg, "Forming reference to null pointer", ErrorNode);
209249261Sdim  if (ArgE) {
210249261Sdim    const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE);
211276479Sdim    if (!ArgEDeref)
212249261Sdim      ArgEDeref = ArgE;
213249261Sdim    bugreporter::trackNullOrUndefValue(ErrorNode,
214249261Sdim                                       ArgEDeref,
215249261Sdim                                       *R);
216249261Sdim  }
217249261Sdim  return R;
218249261Sdim
219249261Sdim}
220249261Sdim
221249261Sdimvoid ento::registerNonNullParamChecker(CheckerManager &mgr) {
222249261Sdim  mgr.registerChecker<NonNullParamChecker>();
223249261Sdim}
224