NonNullParamChecker.cpp revision 341825
1286171Smarkj//===--- NonNullParamChecker.cpp - Undefined arguments checker -*- C++ -*--===//
2210753Srpaulo//
3210753Srpaulo//                     The LLVM Compiler Infrastructure
4210753Srpaulo//
5210753Srpaulo// This file is distributed under the University of Illinois Open Source
6210753Srpaulo// License. See LICENSE.TXT for details.
7210753Srpaulo//
8210753Srpaulo//===----------------------------------------------------------------------===//
9210753Srpaulo//
10210753Srpaulo// This defines NonNullParamChecker, which checks for arguments expected not to
11210753Srpaulo// be null due to:
12210753Srpaulo//   - the corresponding parameters being declared to have nonnull attribute
13210753Srpaulo//   - the corresponding parameters being references; since the call would form
14210753Srpaulo//     a reference to a null pointer
15210753Srpaulo//
16210753Srpaulo//===----------------------------------------------------------------------===//
17210753Srpaulo
18210753Srpaulo#include "ClangSACheckers.h"
19210753Srpaulo#include "clang/AST/Attr.h"
20210753Srpaulo#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
21210753Srpaulo#include "clang/StaticAnalyzer/Core/Checker.h"
22210753Srpaulo#include "clang/StaticAnalyzer/Core/CheckerManager.h"
23210753Srpaulo#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
24210753Srpaulo#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
25210753Srpaulo
26210753Srpaulousing namespace clang;
27210753Srpaulousing namespace ento;
28210753Srpaulo
29210753Srpaulonamespace {
30210753Srpauloclass NonNullParamChecker
31210753Srpaulo  : public Checker< check::PreCall, EventDispatcher<ImplicitNullDerefEvent> > {
32210753Srpaulo  mutable std::unique_ptr<BugType> BTAttrNonNull;
33210753Srpaulo  mutable std::unique_ptr<BugType> BTNullRefArg;
34210753Srpaulo
35210753Srpaulopublic:
36210753Srpaulo
37210753Srpaulo  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
38210753Srpaulo
39210753Srpaulo  std::unique_ptr<BugReport>
40210753Srpaulo  genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE) const;
41210753Srpaulo  std::unique_ptr<BugReport>
42210753Srpaulo  genReportReferenceToNullPointer(const ExplodedNode *ErrorN,
43210753Srpaulo                                  const Expr *ArgE) const;
44210753Srpaulo};
45210753Srpaulo} // end anonymous namespace
46210753Srpaulo
47210753Srpaulo/// \return Bitvector marking non-null attributes.
48210753Srpaulostatic llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) {
49210753Srpaulo  const Decl *FD = Call.getDecl();
50210753Srpaulo  unsigned NumArgs = Call.getNumArgs();
51250685Smarkj  llvm::SmallBitVector AttrNonNull(NumArgs);
52210753Srpaulo  for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) {
53250685Smarkj    if (!NonNull->args_size()) {
54210753Srpaulo      AttrNonNull.set(0, NumArgs);
55211545Srpaulo      break;
56210753Srpaulo    }
57210753Srpaulo    for (const ParamIdx &Idx : NonNull->args()) {
58210753Srpaulo      unsigned IdxAST = Idx.getASTIndex();
59210753Srpaulo      if (IdxAST >= NumArgs)
60210753Srpaulo        continue;
61210753Srpaulo      AttrNonNull.set(IdxAST);
62210753Srpaulo    }
63210753Srpaulo  }
64250685Smarkj  return AttrNonNull;
65250685Smarkj}
66250685Smarkj
67250685Smarkjvoid NonNullParamChecker::checkPreCall(const CallEvent &Call,
68210753Srpaulo                                       CheckerContext &C) const {
69250685Smarkj  if (!Call.getDecl())
70210753Srpaulo    return;
71210753Srpaulo
72250685Smarkj  llvm::SmallBitVector AttrNonNull = getNonNullAttrs(Call);
73210753Srpaulo  unsigned NumArgs = Call.getNumArgs();
74250685Smarkj
75210753Srpaulo  ProgramStateRef state = C.getState();
76210753Srpaulo  ArrayRef<ParmVarDecl*> parms = Call.parameters();
77210753Srpaulo
78210753Srpaulo  for (unsigned idx = 0; idx < NumArgs; ++idx) {
79210753Srpaulo    // For vararg functions, a corresponding parameter decl may not exist.
80210753Srpaulo    bool HasParam = idx < parms.size();
81250685Smarkj
82210753Srpaulo    // Check if the parameter is a reference. We want to report when reference
83250685Smarkj    // to a null pointer is passed as a parameter.
84250685Smarkj    bool haveRefTypeParam =
85250685Smarkj        HasParam ? parms[idx]->getType()->isReferenceType() : false;
86250685Smarkj    bool haveAttrNonNull = AttrNonNull[idx];
87250685Smarkj
88250685Smarkj    // Check if the parameter is also marked 'nonnull'.
89250685Smarkj    if (!haveAttrNonNull && HasParam)
90250685Smarkj      haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>();
91250685Smarkj
92210753Srpaulo    if (!haveAttrNonNull && !haveRefTypeParam)
93210753Srpaulo      continue;
94210753Srpaulo
95210753Srpaulo    // If the value is unknown or undefined, we can't perform this check.
96210753Srpaulo    const Expr *ArgE = Call.getArgExpr(idx);
97210753Srpaulo    SVal V = Call.getArgSVal(idx);
98    auto DV = V.getAs<DefinedSVal>();
99    if (!DV)
100      continue;
101
102    assert(!haveRefTypeParam || DV->getAs<Loc>());
103
104    // Process the case when the argument is not a location.
105    if (haveAttrNonNull && !DV->getAs<Loc>()) {
106      // If the argument is a union type, we want to handle a potential
107      // transparent_union GCC extension.
108      if (!ArgE)
109        continue;
110
111      QualType T = ArgE->getType();
112      const RecordType *UT = T->getAsUnionType();
113      if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>())
114        continue;
115
116      auto CSV = DV->getAs<nonloc::CompoundVal>();
117
118      // FIXME: Handle LazyCompoundVals?
119      if (!CSV)
120        continue;
121
122      V = *(CSV->begin());
123      DV = V.getAs<DefinedSVal>();
124      assert(++CSV->begin() == CSV->end());
125      // FIXME: Handle (some_union){ some_other_union_val }, which turns into
126      // a LazyCompoundVal inside a CompoundVal.
127      if (!V.getAs<Loc>())
128        continue;
129
130      // Retrieve the corresponding expression.
131      if (const auto *CE = dyn_cast<CompoundLiteralExpr>(ArgE))
132        if (const auto *IE = dyn_cast<InitListExpr>(CE->getInitializer()))
133          ArgE = dyn_cast<Expr>(*(IE->begin()));
134    }
135
136    ConstraintManager &CM = C.getConstraintManager();
137    ProgramStateRef stateNotNull, stateNull;
138    std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
139
140    // Generate an error node.  Check for a null node in case
141    // we cache out.
142    if (stateNull && !stateNotNull) {
143      if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) {
144
145        std::unique_ptr<BugReport> R;
146        if (haveAttrNonNull)
147          R = genReportNullAttrNonNull(errorNode, ArgE);
148        else if (haveRefTypeParam)
149          R = genReportReferenceToNullPointer(errorNode, ArgE);
150
151        // Highlight the range of the argument that was null.
152        R->addRange(Call.getArgSourceRange(idx));
153
154        // Emit the bug report.
155        C.emitReport(std::move(R));
156      }
157
158      // Always return.  Either we cached out or we just emitted an error.
159      return;
160    }
161
162    if (stateNull) {
163      if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) {
164        ImplicitNullDerefEvent event = {
165          V, false, N, &C.getBugReporter(),
166          /*IsDirectDereference=*/haveRefTypeParam};
167        dispatchEvent(event);
168      }
169    }
170
171    // If a pointer value passed the check we should assume that it is
172    // indeed not null from this point forward.
173    state = stateNotNull;
174  }
175
176  // If we reach here all of the arguments passed the nonnull check.
177  // If 'state' has been updated generated a new node.
178  C.addTransition(state);
179}
180
181std::unique_ptr<BugReport>
182NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode,
183                                              const Expr *ArgE) const {
184  // Lazily allocate the BugType object if it hasn't already been
185  // created. Ownership is transferred to the BugReporter object once
186  // the BugReport is passed to 'EmitWarning'.
187  if (!BTAttrNonNull)
188    BTAttrNonNull.reset(new BugType(
189        this, "Argument with 'nonnull' attribute passed null", "API"));
190
191  auto R = llvm::make_unique<BugReport>(
192      *BTAttrNonNull,
193      "Null pointer passed as an argument to a 'nonnull' parameter", ErrorNode);
194  if (ArgE)
195    bugreporter::trackNullOrUndefValue(ErrorNode, ArgE, *R);
196
197  return R;
198}
199
200std::unique_ptr<BugReport> NonNullParamChecker::genReportReferenceToNullPointer(
201    const ExplodedNode *ErrorNode, const Expr *ArgE) const {
202  if (!BTNullRefArg)
203    BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer"));
204
205  auto R = llvm::make_unique<BugReport>(
206      *BTNullRefArg, "Forming reference to null pointer", ErrorNode);
207  if (ArgE) {
208    const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE);
209    if (!ArgEDeref)
210      ArgEDeref = ArgE;
211    bugreporter::trackNullOrUndefValue(ErrorNode,
212                                       ArgEDeref,
213                                       *R);
214  }
215  return R;
216
217}
218
219void ento::registerNonNullParamChecker(CheckerManager &mgr) {
220  mgr.registerChecker<NonNullParamChecker>();
221}
222