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