NonNullParamChecker.cpp revision 6bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89
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  BugReport *genReportNullAttrNonNull(const ExplodedNode *ErrorN,
40                                      const Expr *ArgE) const;
41  BugReport *genReportReferenceToNullPointer(const ExplodedNode *ErrorN,
42                                             const Expr *ArgE) const;
43};
44} // end anonymous namespace
45
46void NonNullParamChecker::checkPreCall(const CallEvent &Call,
47                                       CheckerContext &C) const {
48  const Decl *FD = Call.getDecl();
49  if (!FD)
50    return;
51
52  const NonNullAttr *Att = FD->getAttr<NonNullAttr>();
53
54  ProgramStateRef state = C.getState();
55
56  CallEvent::param_type_iterator TyI = Call.param_type_begin(),
57                                 TyE = Call.param_type_end();
58
59  for (unsigned idx = 0, count = Call.getNumArgs(); idx != count; ++idx){
60
61    // Check if the parameter is a reference. We want to report when reference
62    // to a null pointer is passed as a paramter.
63    bool haveRefTypeParam = false;
64    if (TyI != TyE) {
65      haveRefTypeParam = (*TyI)->isReferenceType();
66      TyI++;
67    }
68
69    bool haveAttrNonNull = Att && Att->isNonNull(idx);
70    if (!haveAttrNonNull) {
71      // Check if the parameter is also marked 'nonnull'.
72      ArrayRef<ParmVarDecl*> parms = Call.parameters();
73      if (idx < parms.size())
74        haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>();
75    }
76
77    if (!haveRefTypeParam && !haveAttrNonNull)
78      continue;
79
80    // If the value is unknown or undefined, we can't perform this check.
81    const Expr *ArgE = Call.getArgExpr(idx);
82    SVal V = Call.getArgSVal(idx);
83    Optional<DefinedSVal> DV = V.getAs<DefinedSVal>();
84    if (!DV)
85      continue;
86
87    // Process the case when the argument is not a location.
88    assert(!haveRefTypeParam || DV->getAs<Loc>());
89
90    if (haveAttrNonNull && !DV->getAs<Loc>()) {
91      // If the argument is a union type, we want to handle a potential
92      // transparent_union GCC extension.
93      if (!ArgE)
94        continue;
95
96      QualType T = ArgE->getType();
97      const RecordType *UT = T->getAsUnionType();
98      if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>())
99        continue;
100
101      if (Optional<nonloc::CompoundVal> CSV =
102              DV->getAs<nonloc::CompoundVal>()) {
103        nonloc::CompoundVal::iterator CSV_I = CSV->begin();
104        assert(CSV_I != CSV->end());
105        V = *CSV_I;
106        DV = V.getAs<DefinedSVal>();
107        assert(++CSV_I == CSV->end());
108        // FIXME: Handle (some_union){ some_other_union_val }, which turns into
109        // a LazyCompoundVal inside a CompoundVal.
110        if (!V.getAs<Loc>())
111          continue;
112        // Retrieve the corresponding expression.
113        if (const CompoundLiteralExpr *CE = dyn_cast<CompoundLiteralExpr>(ArgE))
114          if (const InitListExpr *IE =
115                dyn_cast<InitListExpr>(CE->getInitializer()))
116             ArgE = dyn_cast<Expr>(*(IE->begin()));
117
118      } else {
119        // FIXME: Handle LazyCompoundVals?
120        continue;
121      }
122    }
123
124    ConstraintManager &CM = C.getConstraintManager();
125    ProgramStateRef stateNotNull, stateNull;
126    std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
127
128    if (stateNull && !stateNotNull) {
129      // Generate an error node.  Check for a null node in case
130      // we cache out.
131      if (ExplodedNode *errorNode = C.generateSink(stateNull)) {
132
133        BugReport *R = nullptr;
134        if (haveAttrNonNull)
135          R = genReportNullAttrNonNull(errorNode, ArgE);
136        else if (haveRefTypeParam)
137          R = genReportReferenceToNullPointer(errorNode, ArgE);
138
139        // Highlight the range of the argument that was null.
140        R->addRange(Call.getArgSourceRange(idx));
141
142        // Emit the bug report.
143        C.emitReport(R);
144      }
145
146      // Always return.  Either we cached out or we just emitted an error.
147      return;
148    }
149
150    // If a pointer value passed the check we should assume that it is
151    // indeed not null from this point forward.
152    assert(stateNotNull);
153    state = stateNotNull;
154  }
155
156  // If we reach here all of the arguments passed the nonnull check.
157  // If 'state' has been updated generated a new node.
158  C.addTransition(state);
159}
160
161BugReport *NonNullParamChecker::genReportNullAttrNonNull(
162  const ExplodedNode *ErrorNode, const Expr *ArgE) const {
163  // Lazily allocate the BugType object if it hasn't already been
164  // created. Ownership is transferred to the BugReporter object once
165  // the BugReport is passed to 'EmitWarning'.
166  if (!BTAttrNonNull)
167    BTAttrNonNull.reset(new BugType(
168        this, "Argument with 'nonnull' attribute passed null", "API"));
169
170  BugReport *R = new BugReport(*BTAttrNonNull,
171                  "Null pointer passed as an argument to a 'nonnull' parameter",
172                  ErrorNode);
173  if (ArgE)
174    bugreporter::trackNullOrUndefValue(ErrorNode, ArgE, *R);
175
176  return R;
177}
178
179BugReport *NonNullParamChecker::genReportReferenceToNullPointer(
180  const ExplodedNode *ErrorNode, const Expr *ArgE) const {
181  if (!BTNullRefArg)
182    BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer"));
183
184  BugReport *R = new BugReport(*BTNullRefArg,
185                               "Forming reference to null pointer",
186                               ErrorNode);
187  if (ArgE) {
188    const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE);
189    if (!ArgEDeref)
190      ArgEDeref = ArgE;
191    bugreporter::trackNullOrUndefValue(ErrorNode,
192                                       ArgEDeref,
193                                       *R);
194  }
195  return R;
196
197}
198
199void ento::registerNonNullParamChecker(CheckerManager &mgr) {
200  mgr.registerChecker<NonNullParamChecker>();
201}
202