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