NonNullParamChecker.cpp revision 296417
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, EventDispatcher<ImplicitNullDerefEvent> > {
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) {
143      if (!stateNotNull) {
144        // Generate an error node.  Check for a null node in case
145        // we cache out.
146        if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) {
147
148          std::unique_ptr<BugReport> R;
149          if (haveAttrNonNull)
150            R = genReportNullAttrNonNull(errorNode, ArgE);
151          else if (haveRefTypeParam)
152            R = genReportReferenceToNullPointer(errorNode, ArgE);
153
154          // Highlight the range of the argument that was null.
155          R->addRange(Call.getArgSourceRange(idx));
156
157          // Emit the bug report.
158          C.emitReport(std::move(R));
159        }
160
161        // Always return.  Either we cached out or we just emitted an error.
162        return;
163      }
164      if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) {
165        ImplicitNullDerefEvent event = {
166            V, false, N, &C.getBugReporter(),
167            /*IsDirectDereference=*/haveRefTypeParam};
168        dispatchEvent(event);
169      }
170    }
171
172    // If a pointer value passed the check we should assume that it is
173    // indeed not null from this point forward.
174    assert(stateNotNull);
175    state = stateNotNull;
176  }
177
178  // If we reach here all of the arguments passed the nonnull check.
179  // If 'state' has been updated generated a new node.
180  C.addTransition(state);
181}
182
183std::unique_ptr<BugReport>
184NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode,
185                                              const Expr *ArgE) const {
186  // Lazily allocate the BugType object if it hasn't already been
187  // created. Ownership is transferred to the BugReporter object once
188  // the BugReport is passed to 'EmitWarning'.
189  if (!BTAttrNonNull)
190    BTAttrNonNull.reset(new BugType(
191        this, "Argument with 'nonnull' attribute passed null", "API"));
192
193  auto R = llvm::make_unique<BugReport>(
194      *BTAttrNonNull,
195      "Null pointer passed as an argument to a 'nonnull' parameter", ErrorNode);
196  if (ArgE)
197    bugreporter::trackNullOrUndefValue(ErrorNode, ArgE, *R);
198
199  return R;
200}
201
202std::unique_ptr<BugReport> NonNullParamChecker::genReportReferenceToNullPointer(
203    const ExplodedNode *ErrorNode, const Expr *ArgE) const {
204  if (!BTNullRefArg)
205    BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer"));
206
207  auto R = llvm::make_unique<BugReport>(
208      *BTNullRefArg, "Forming reference to null pointer", ErrorNode);
209  if (ArgE) {
210    const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE);
211    if (!ArgEDeref)
212      ArgEDeref = ArgE;
213    bugreporter::trackNullOrUndefValue(ErrorNode,
214                                       ArgEDeref,
215                                       *R);
216  }
217  return R;
218
219}
220
221void ento::registerNonNullParamChecker(CheckerManager &mgr) {
222  mgr.registerChecker<NonNullParamChecker>();
223}
224