NonNullParamChecker.cpp revision 288943
1//===--- NonNullParamChecker.cpp - Undefined arguments checker -*- C++ -*--===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This defines NonNullParamChecker, which checks for arguments expected not to
11// be null due to:
12//   - the corresponding parameters being declared to have nonnull attribute
13//   - the corresponding parameters being references; since the call would form
14//     a reference to a null pointer
15//
16//===----------------------------------------------------------------------===//
17
18#include "ClangSACheckers.h"
19#include "clang/AST/Attr.h"
20#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
21#include "clang/StaticAnalyzer/Core/Checker.h"
22#include "clang/StaticAnalyzer/Core/CheckerManager.h"
23#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
24#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
25
26using namespace clang;
27using namespace ento;
28
29namespace {
30class NonNullParamChecker
31  : public Checker< check::PreCall > {
32  mutable std::unique_ptr<BugType> BTAttrNonNull;
33  mutable std::unique_ptr<BugType> BTNullRefArg;
34
35public:
36
37  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
38
39  std::unique_ptr<BugReport>
40  genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE) const;
41  std::unique_ptr<BugReport>
42  genReportReferenceToNullPointer(const ExplodedNode *ErrorN,
43                                  const Expr *ArgE) const;
44};
45} // end anonymous namespace
46
47void NonNullParamChecker::checkPreCall(const CallEvent &Call,
48                                       CheckerContext &C) const {
49  const Decl *FD = Call.getDecl();
50  if (!FD)
51    return;
52
53  // Merge all non-null attributes
54  unsigned NumArgs = Call.getNumArgs();
55  llvm::SmallBitVector AttrNonNull(NumArgs);
56  for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) {
57    if (!NonNull->args_size()) {
58      AttrNonNull.set(0, NumArgs);
59      break;
60    }
61    for (unsigned Val : NonNull->args()) {
62      if (Val >= NumArgs)
63        continue;
64      AttrNonNull.set(Val);
65    }
66  }
67
68  ProgramStateRef state = C.getState();
69
70  CallEvent::param_type_iterator TyI = Call.param_type_begin(),
71                                 TyE = Call.param_type_end();
72
73  for (unsigned idx = 0; idx < NumArgs; ++idx) {
74
75    // Check if the parameter is a reference. We want to report when reference
76    // to a null pointer is passed as a paramter.
77    bool haveRefTypeParam = false;
78    if (TyI != TyE) {
79      haveRefTypeParam = (*TyI)->isReferenceType();
80      TyI++;
81    }
82
83    bool haveAttrNonNull = AttrNonNull[idx];
84    if (!haveAttrNonNull) {
85      // Check if the parameter is also marked 'nonnull'.
86      ArrayRef<ParmVarDecl*> parms = Call.parameters();
87      if (idx < parms.size())
88        haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>();
89    }
90
91    if (!haveRefTypeParam && !haveAttrNonNull)
92      continue;
93
94    // If the value is unknown or undefined, we can't perform this check.
95    const Expr *ArgE = Call.getArgExpr(idx);
96    SVal V = Call.getArgSVal(idx);
97    Optional<DefinedSVal> DV = V.getAs<DefinedSVal>();
98    if (!DV)
99      continue;
100
101    // Process the case when the argument is not a location.
102    assert(!haveRefTypeParam || DV->getAs<Loc>());
103
104    if (haveAttrNonNull && !DV->getAs<Loc>()) {
105      // If the argument is a union type, we want to handle a potential
106      // transparent_union GCC extension.
107      if (!ArgE)
108        continue;
109
110      QualType T = ArgE->getType();
111      const RecordType *UT = T->getAsUnionType();
112      if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>())
113        continue;
114
115      if (Optional<nonloc::CompoundVal> CSV =
116              DV->getAs<nonloc::CompoundVal>()) {
117        nonloc::CompoundVal::iterator CSV_I = CSV->begin();
118        assert(CSV_I != CSV->end());
119        V = *CSV_I;
120        DV = V.getAs<DefinedSVal>();
121        assert(++CSV_I == CSV->end());
122        // FIXME: Handle (some_union){ some_other_union_val }, which turns into
123        // a LazyCompoundVal inside a CompoundVal.
124        if (!V.getAs<Loc>())
125          continue;
126        // Retrieve the corresponding expression.
127        if (const CompoundLiteralExpr *CE = dyn_cast<CompoundLiteralExpr>(ArgE))
128          if (const InitListExpr *IE =
129                dyn_cast<InitListExpr>(CE->getInitializer()))
130             ArgE = dyn_cast<Expr>(*(IE->begin()));
131
132      } else {
133        // FIXME: Handle LazyCompoundVals?
134        continue;
135      }
136    }
137
138    ConstraintManager &CM = C.getConstraintManager();
139    ProgramStateRef stateNotNull, stateNull;
140    std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
141
142    if (stateNull && !stateNotNull) {
143      // Generate an error node.  Check for a null node in case
144      // we cache out.
145      if (ExplodedNode *errorNode = C.generateSink(stateNull)) {
146
147        std::unique_ptr<BugReport> R;
148        if (haveAttrNonNull)
149          R = genReportNullAttrNonNull(errorNode, ArgE);
150        else if (haveRefTypeParam)
151          R = genReportReferenceToNullPointer(errorNode, ArgE);
152
153        // Highlight the range of the argument that was null.
154        R->addRange(Call.getArgSourceRange(idx));
155
156        // Emit the bug report.
157        C.emitReport(std::move(R));
158      }
159
160      // Always return.  Either we cached out or we just emitted an error.
161      return;
162    }
163
164    // If a pointer value passed the check we should assume that it is
165    // indeed not null from this point forward.
166    assert(stateNotNull);
167    state = stateNotNull;
168  }
169
170  // If we reach here all of the arguments passed the nonnull check.
171  // If 'state' has been updated generated a new node.
172  C.addTransition(state);
173}
174
175std::unique_ptr<BugReport>
176NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode,
177                                              const Expr *ArgE) const {
178  // Lazily allocate the BugType object if it hasn't already been
179  // created. Ownership is transferred to the BugReporter object once
180  // the BugReport is passed to 'EmitWarning'.
181  if (!BTAttrNonNull)
182    BTAttrNonNull.reset(new BugType(
183        this, "Argument with 'nonnull' attribute passed null", "API"));
184
185  auto R = llvm::make_unique<BugReport>(
186      *BTAttrNonNull,
187      "Null pointer passed as an argument to a 'nonnull' parameter", ErrorNode);
188  if (ArgE)
189    bugreporter::trackNullOrUndefValue(ErrorNode, ArgE, *R);
190
191  return R;
192}
193
194std::unique_ptr<BugReport> NonNullParamChecker::genReportReferenceToNullPointer(
195    const ExplodedNode *ErrorNode, const Expr *ArgE) const {
196  if (!BTNullRefArg)
197    BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer"));
198
199  auto R = llvm::make_unique<BugReport>(
200      *BTNullRefArg, "Forming reference to null pointer", ErrorNode);
201  if (ArgE) {
202    const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE);
203    if (!ArgEDeref)
204      ArgEDeref = ArgE;
205    bugreporter::trackNullOrUndefValue(ErrorNode,
206                                       ArgEDeref,
207                                       *R);
208  }
209  return R;
210
211}
212
213void ento::registerNonNullParamChecker(CheckerManager &mgr) {
214  mgr.registerChecker<NonNullParamChecker>();
215}
216