]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / llvm / tools / clang / lib / StaticAnalyzer / Checkers / NonNullParamChecker.cpp
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
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
152 BugReport *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
171 BugReport *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
191 void ento::registerNonNullParamChecker(CheckerManager &mgr) {
192   mgr.registerChecker<NonNullParamChecker>();
193 }