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         if (!DV)
108           continue;
109         // Retrieve the corresponding expression.
110         if (const CompoundLiteralExpr *CE = dyn_cast<CompoundLiteralExpr>(ArgE))
111           if (const InitListExpr *IE =
112                 dyn_cast<InitListExpr>(CE->getInitializer()))
113              ArgE = dyn_cast<Expr>(*(IE->begin()));
114 
115       } else {
116         // FIXME: Handle LazyCompoundVals?
117         continue;
118       }
119     }
120 
121     ConstraintManager &CM = C.getConstraintManager();
122     ProgramStateRef stateNotNull, stateNull;
123     llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
124 
125     if (stateNull && !stateNotNull) {
126       // Generate an error node.  Check for a null node in case
127       // we cache out.
128       if (ExplodedNode *errorNode = C.generateSink(stateNull)) {
129 
130         BugReport *R = 0;
131         if (haveAttrNonNull)
132           R = genReportNullAttrNonNull(errorNode, ArgE);
133         else if (haveRefTypeParam)
134           R = genReportReferenceToNullPointer(errorNode, ArgE);
135 
136         // Highlight the range of the argument that was null.
137         R->addRange(Call.getArgSourceRange(idx));
138 
139         // Emit the bug report.
140         C.emitReport(R);
141       }
142 
143       // Always return.  Either we cached out or we just emitted an error.
144       return;
145     }
146 
147     // If a pointer value passed the check we should assume that it is
148     // indeed not null from this point forward.
149     assert(stateNotNull);
150     state = stateNotNull;
151   }
152 
153   // If we reach here all of the arguments passed the nonnull check.
154   // If 'state' has been updated generated a new node.
155   C.addTransition(state);
156 }
157 
158 BugReport *NonNullParamChecker::genReportNullAttrNonNull(
159   const ExplodedNode *ErrorNode, const Expr *ArgE) const {
160   // Lazily allocate the BugType object if it hasn't already been
161   // created. Ownership is transferred to the BugReporter object once
162   // the BugReport is passed to 'EmitWarning'.
163   if (!BTAttrNonNull)
164     BTAttrNonNull.reset(new BugType(
165                             "Argument with 'nonnull' attribute passed null",
166                             "API"));
167 
168   BugReport *R = new BugReport(*BTAttrNonNull,
169                   "Null pointer passed as an argument to a 'nonnull' parameter",
170                   ErrorNode);
171   if (ArgE)
172     bugreporter::trackNullOrUndefValue(ErrorNode, ArgE, *R);
173 
174   return R;
175 }
176 
177 BugReport *NonNullParamChecker::genReportReferenceToNullPointer(
178   const ExplodedNode *ErrorNode, const Expr *ArgE) const {
179   if (!BTNullRefArg)
180     BTNullRefArg.reset(new BuiltinBug("Dereference of null pointer"));
181 
182   BugReport *R = new BugReport(*BTNullRefArg,
183                                "Forming reference to null pointer",
184                                ErrorNode);
185   if (ArgE) {
186     const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE);
187     if (ArgEDeref == 0)
188       ArgEDeref = ArgE;
189     bugreporter::trackNullOrUndefValue(ErrorNode,
190                                        ArgEDeref,
191                                        *R);
192   }
193   return R;
194 
195 }
196 
197 void ento::registerNonNullParamChecker(CheckerManager &mgr) {
198   mgr.registerChecker<NonNullParamChecker>();
199 }
200