/Users/buildslave/jenkins/sharedspace/clang-stage2-coverage-R@2/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
Line | Count | Source (jump to first uncovered line) |
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, EventDispatcher<ImplicitNullDerefEvent> > { |
32 | | mutable std::unique_ptr<BugType> BTAttrNonNull; |
33 | | mutable std::unique_ptr<BugType> BTNullRefArg; |
34 | | |
35 | | public: |
36 | | |
37 | | void checkPreCall(const CallEvent &Call, CheckerContext &C) const; |
38 | | |
39 | | std::unique_ptr<BugReport> |
40 | | genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE) const; |
41 | | std::unique_ptr<BugReport> |
42 | | genReportReferenceToNullPointer(const ExplodedNode *ErrorN, |
43 | | const Expr *ArgE) const; |
44 | | }; |
45 | | } // end anonymous namespace |
46 | | |
47 | | void NonNullParamChecker::checkPreCall(const CallEvent &Call, |
48 | 47.5k | CheckerContext &C) const { |
49 | 47.5k | const Decl *FD = Call.getDecl(); |
50 | 47.5k | if (!FD) |
51 | 36 | return; |
52 | 47.5k | |
53 | 47.5k | // Merge all non-null attributes |
54 | 47.5k | unsigned NumArgs = Call.getNumArgs(); |
55 | 47.5k | llvm::SmallBitVector AttrNonNull(NumArgs); |
56 | 83 | for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) { |
57 | 83 | if (!NonNull->args_size()83 ) { |
58 | 39 | AttrNonNull.set(0, NumArgs); |
59 | 39 | break; |
60 | 39 | } |
61 | 44 | for (unsigned Val : NonNull->args()) 44 { |
62 | 50 | if (Val >= NumArgs) |
63 | 0 | continue; |
64 | 50 | AttrNonNull.set(Val); |
65 | 50 | } |
66 | 83 | } |
67 | 47.5k | |
68 | 47.5k | ProgramStateRef state = C.getState(); |
69 | 47.5k | |
70 | 47.5k | CallEvent::param_type_iterator TyI = Call.param_type_begin(), |
71 | 47.5k | TyE = Call.param_type_end(); |
72 | 47.5k | |
73 | 92.3k | for (unsigned idx = 0; idx < NumArgs92.3k ; ++idx44.8k ) { |
74 | 44.8k | |
75 | 44.8k | // Check if the parameter is a reference. We want to report when reference |
76 | 44.8k | // to a null pointer is passed as a parameter. |
77 | 44.8k | bool haveRefTypeParam = false; |
78 | 44.8k | if (TyI != TyE44.8k ) { |
79 | 44.4k | haveRefTypeParam = (*TyI)->isReferenceType(); |
80 | 44.4k | TyI++; |
81 | 44.4k | } |
82 | 44.8k | |
83 | 44.8k | bool haveAttrNonNull = AttrNonNull[idx]; |
84 | 44.8k | if (!haveAttrNonNull44.8k ) { |
85 | 44.7k | // Check if the parameter is also marked 'nonnull'. |
86 | 44.7k | ArrayRef<ParmVarDecl*> parms = Call.parameters(); |
87 | 44.7k | if (idx < parms.size()) |
88 | 44.3k | haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>(); |
89 | 44.7k | } |
90 | 44.8k | |
91 | 44.8k | if (!haveRefTypeParam && 44.8k !haveAttrNonNull28.6k ) |
92 | 28.5k | continue; |
93 | 16.2k | |
94 | 16.2k | // If the value is unknown or undefined, we can't perform this check. |
95 | 16.2k | const Expr *ArgE = Call.getArgExpr(idx); |
96 | 16.2k | SVal V = Call.getArgSVal(idx); |
97 | 16.2k | Optional<DefinedSVal> DV = V.getAs<DefinedSVal>(); |
98 | 16.2k | if (!DV) |
99 | 3 | continue; |
100 | 16.2k | |
101 | 16.2k | // Process the case when the argument is not a location. |
102 | 16.2k | assert(!haveRefTypeParam || DV->getAs<Loc>()); |
103 | 16.2k | |
104 | 16.2k | if (haveAttrNonNull && 16.2k !DV->getAs<Loc>()97 ) { |
105 | 23 | // If the argument is a union type, we want to handle a potential |
106 | 23 | // transparent_union GCC extension. |
107 | 23 | if (!ArgE) |
108 | 0 | continue; |
109 | 23 | |
110 | 23 | QualType T = ArgE->getType(); |
111 | 23 | const RecordType *UT = T->getAsUnionType(); |
112 | 23 | if (!UT || 23 !UT->getDecl()->hasAttr<TransparentUnionAttr>()9 ) |
113 | 14 | continue; |
114 | 9 | |
115 | 9 | if (Optional<nonloc::CompoundVal> 9 CSV9 = |
116 | 8 | DV->getAs<nonloc::CompoundVal>()) { |
117 | 8 | nonloc::CompoundVal::iterator CSV_I = CSV->begin(); |
118 | 8 | assert(CSV_I != CSV->end()); |
119 | 8 | V = *CSV_I; |
120 | 8 | DV = V.getAs<DefinedSVal>(); |
121 | 8 | assert(++CSV_I == CSV->end()); |
122 | 8 | // FIXME: Handle (some_union){ some_other_union_val }, which turns into |
123 | 8 | // a LazyCompoundVal inside a CompoundVal. |
124 | 8 | if (!V.getAs<Loc>()) |
125 | 1 | continue; |
126 | 7 | // Retrieve the corresponding expression. |
127 | 7 | if (const CompoundLiteralExpr *7 CE7 = dyn_cast<CompoundLiteralExpr>(ArgE)) |
128 | 7 | if (const InitListExpr *7 IE7 = |
129 | 7 | dyn_cast<InitListExpr>(CE->getInitializer())) |
130 | 7 | ArgE = dyn_cast<Expr>(*(IE->begin())); |
131 | 8 | |
132 | 9 | } else { |
133 | 1 | // FIXME: Handle LazyCompoundVals? |
134 | 1 | continue; |
135 | 1 | } |
136 | 16.2k | } |
137 | 16.2k | |
138 | 16.2k | ConstraintManager &CM = C.getConstraintManager(); |
139 | 16.2k | ProgramStateRef stateNotNull, stateNull; |
140 | 16.2k | std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); |
141 | 16.2k | |
142 | 16.2k | if (stateNull16.2k ) { |
143 | 7.69k | if (!stateNotNull7.69k ) { |
144 | 39 | // Generate an error node. Check for a null node in case |
145 | 39 | // we cache out. |
146 | 39 | if (ExplodedNode *errorNode39 = C.generateErrorNode(stateNull)) { |
147 | 39 | |
148 | 39 | std::unique_ptr<BugReport> R; |
149 | 39 | if (haveAttrNonNull) |
150 | 32 | R = genReportNullAttrNonNull(errorNode, ArgE); |
151 | 7 | else if (7 haveRefTypeParam7 ) |
152 | 7 | R = genReportReferenceToNullPointer(errorNode, ArgE); |
153 | 39 | |
154 | 39 | // Highlight the range of the argument that was null. |
155 | 39 | R->addRange(Call.getArgSourceRange(idx)); |
156 | 39 | |
157 | 39 | // Emit the bug report. |
158 | 39 | C.emitReport(std::move(R)); |
159 | 39 | } |
160 | 39 | |
161 | 39 | // Always return. Either we cached out or we just emitted an error. |
162 | 39 | return; |
163 | 39 | } |
164 | 7.66k | if (ExplodedNode *7.66k N7.66k = C.generateSink(stateNull, C.getPredecessor())) { |
165 | 7.66k | ImplicitNullDerefEvent event = { |
166 | 7.66k | V, false, N, &C.getBugReporter(), |
167 | 7.66k | /*IsDirectDereference=*/haveRefTypeParam}; |
168 | 7.66k | dispatchEvent(event); |
169 | 7.66k | } |
170 | 7.69k | } |
171 | 16.2k | |
172 | 16.2k | // If a pointer value passed the check we should assume that it is |
173 | 16.2k | // indeed not null from this point forward. |
174 | 16.2k | assert(stateNotNull); |
175 | 16.2k | state = stateNotNull; |
176 | 16.2k | } |
177 | 47.5k | |
178 | 47.5k | // If we reach here all of the arguments passed the nonnull check. |
179 | 47.5k | // If 'state' has been updated generated a new node. |
180 | 47.5k | C.addTransition(state); |
181 | 47.5k | } |
182 | | |
183 | | std::unique_ptr<BugReport> |
184 | | NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode, |
185 | 32 | const Expr *ArgE) const { |
186 | 32 | // Lazily allocate the BugType object if it hasn't already been |
187 | 32 | // created. Ownership is transferred to the BugReporter object once |
188 | 32 | // the BugReport is passed to 'EmitWarning'. |
189 | 32 | if (!BTAttrNonNull) |
190 | 9 | BTAttrNonNull.reset(new BugType( |
191 | 9 | this, "Argument with 'nonnull' attribute passed null", "API")); |
192 | 32 | |
193 | 32 | auto R = llvm::make_unique<BugReport>( |
194 | 32 | *BTAttrNonNull, |
195 | 32 | "Null pointer passed as an argument to a 'nonnull' parameter", ErrorNode); |
196 | 32 | if (ArgE) |
197 | 32 | bugreporter::trackNullOrUndefValue(ErrorNode, ArgE, *R); |
198 | 32 | |
199 | 32 | return R; |
200 | 32 | } |
201 | | |
202 | | std::unique_ptr<BugReport> NonNullParamChecker::genReportReferenceToNullPointer( |
203 | 7 | const ExplodedNode *ErrorNode, const Expr *ArgE) const { |
204 | 7 | if (!BTNullRefArg) |
205 | 3 | BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer")); |
206 | 7 | |
207 | 7 | auto R = llvm::make_unique<BugReport>( |
208 | 7 | *BTNullRefArg, "Forming reference to null pointer", ErrorNode); |
209 | 7 | if (ArgE7 ) { |
210 | 7 | const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE); |
211 | 7 | if (!ArgEDeref) |
212 | 0 | ArgEDeref = ArgE; |
213 | 7 | bugreporter::trackNullOrUndefValue(ErrorNode, |
214 | 7 | ArgEDeref, |
215 | 7 | *R); |
216 | 7 | } |
217 | 7 | return R; |
218 | 7 | |
219 | 7 | } |
220 | | |
221 | 417 | void ento::registerNonNullParamChecker(CheckerManager &mgr) { |
222 | 417 | mgr.registerChecker<NonNullParamChecker>(); |
223 | 417 | } |