/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===--- NonNullParamChecker.cpp - Undefined arguments checker -*- C++ -*--===// |
2 | | // |
3 | | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | | // See https://llvm.org/LICENSE.txt for license information. |
5 | | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | // |
9 | | // This defines NonNullParamChecker, which checks for arguments expected not to |
10 | | // be null due to: |
11 | | // - the corresponding parameters being declared to have nonnull attribute |
12 | | // - the corresponding parameters being references; since the call would form |
13 | | // a reference to a null pointer |
14 | | // |
15 | | //===----------------------------------------------------------------------===// |
16 | | |
17 | | #include "clang/AST/Attr.h" |
18 | | #include "clang/Analysis/AnyCall.h" |
19 | | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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 | | #include "llvm/ADT/StringExtras.h" |
26 | | |
27 | | using namespace clang; |
28 | | using namespace ento; |
29 | | |
30 | | namespace { |
31 | | class NonNullParamChecker |
32 | | : public Checker<check::PreCall, check::BeginFunction, |
33 | | EventDispatcher<ImplicitNullDerefEvent>> { |
34 | | mutable std::unique_ptr<BugType> BTAttrNonNull; |
35 | | mutable std::unique_ptr<BugType> BTNullRefArg; |
36 | | |
37 | | public: |
38 | | void checkPreCall(const CallEvent &Call, CheckerContext &C) const; |
39 | | void checkBeginFunction(CheckerContext &C) const; |
40 | | |
41 | | std::unique_ptr<PathSensitiveBugReport> |
42 | | genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE, |
43 | | unsigned IdxOfArg) const; |
44 | | std::unique_ptr<PathSensitiveBugReport> |
45 | | genReportReferenceToNullPointer(const ExplodedNode *ErrorN, |
46 | | const Expr *ArgE) const; |
47 | | }; |
48 | | |
49 | | template <class CallType> |
50 | | void setBitsAccordingToFunctionAttributes(const CallType &Call, |
51 | 116k | llvm::SmallBitVector &AttrNonNull) { |
52 | 116k | const Decl *FD = Call.getDecl(); |
53 | | |
54 | 116k | for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) { |
55 | 113 | if (!NonNull->args_size()) { |
56 | | // Lack of attribute parameters means that all of the parameters are |
57 | | // implicitly marked as non-null. |
58 | 59 | AttrNonNull.set(); |
59 | 59 | break; |
60 | 59 | } |
61 | | |
62 | 61 | for (const ParamIdx &Idx : NonNull->args())54 { |
63 | | // 'nonnull' attribute's parameters are 1-based and should be adjusted to |
64 | | // match actual AST parameter/argument indices. |
65 | 61 | unsigned IdxAST = Idx.getASTIndex(); |
66 | 61 | if (IdxAST >= AttrNonNull.size()) |
67 | 0 | continue; |
68 | 61 | AttrNonNull.set(IdxAST); |
69 | 61 | } |
70 | 54 | } |
71 | 116k | } NonNullParamChecker.cpp:void (anonymous namespace)::setBitsAccordingToFunctionAttributes<clang::ento::CallEvent>(clang::ento::CallEvent const&, llvm::SmallBitVector&) Line | Count | Source | 51 | 102k | llvm::SmallBitVector &AttrNonNull) { | 52 | 102k | const Decl *FD = Call.getDecl(); | 53 | | | 54 | 102k | for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) { | 55 | 106 | if (!NonNull->args_size()) { | 56 | | // Lack of attribute parameters means that all of the parameters are | 57 | | // implicitly marked as non-null. | 58 | 54 | AttrNonNull.set(); | 59 | 54 | break; | 60 | 54 | } | 61 | | | 62 | 59 | for (const ParamIdx &Idx : NonNull->args())52 { | 63 | | // 'nonnull' attribute's parameters are 1-based and should be adjusted to | 64 | | // match actual AST parameter/argument indices. | 65 | 59 | unsigned IdxAST = Idx.getASTIndex(); | 66 | 59 | if (IdxAST >= AttrNonNull.size()) | 67 | 0 | continue; | 68 | 59 | AttrNonNull.set(IdxAST); | 69 | 59 | } | 70 | 52 | } | 71 | 102k | } |
NonNullParamChecker.cpp:void (anonymous namespace)::setBitsAccordingToFunctionAttributes<clang::AnyCall>(clang::AnyCall const&, llvm::SmallBitVector&) Line | Count | Source | 51 | 14.0k | llvm::SmallBitVector &AttrNonNull) { | 52 | 14.0k | const Decl *FD = Call.getDecl(); | 53 | | | 54 | 14.0k | for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) { | 55 | 7 | if (!NonNull->args_size()) { | 56 | | // Lack of attribute parameters means that all of the parameters are | 57 | | // implicitly marked as non-null. | 58 | 5 | AttrNonNull.set(); | 59 | 5 | break; | 60 | 5 | } | 61 | | | 62 | 2 | for (const ParamIdx &Idx : NonNull->args()) { | 63 | | // 'nonnull' attribute's parameters are 1-based and should be adjusted to | 64 | | // match actual AST parameter/argument indices. | 65 | 2 | unsigned IdxAST = Idx.getASTIndex(); | 66 | 2 | if (IdxAST >= AttrNonNull.size()) | 67 | 0 | continue; | 68 | 2 | AttrNonNull.set(IdxAST); | 69 | 2 | } | 70 | 2 | } | 71 | 14.0k | } |
|
72 | | |
73 | | template <class CallType> |
74 | | void setBitsAccordingToParameterAttributes(const CallType &Call, |
75 | 116k | llvm::SmallBitVector &AttrNonNull) { |
76 | 116k | for (const ParmVarDecl *Parameter : Call.parameters()) { |
77 | 97.4k | unsigned ParameterIndex = Parameter->getFunctionScopeIndex(); |
78 | 97.4k | if (ParameterIndex == AttrNonNull.size()) |
79 | 0 | break; |
80 | | |
81 | 97.4k | if (Parameter->hasAttr<NonNullAttr>()) |
82 | 1 | AttrNonNull.set(ParameterIndex); |
83 | 97.4k | } |
84 | 116k | } NonNullParamChecker.cpp:void (anonymous namespace)::setBitsAccordingToParameterAttributes<clang::ento::CallEvent>(clang::ento::CallEvent const&, llvm::SmallBitVector&) Line | Count | Source | 75 | 102k | llvm::SmallBitVector &AttrNonNull) { | 76 | 102k | for (const ParmVarDecl *Parameter : Call.parameters()) { | 77 | 89.2k | unsigned ParameterIndex = Parameter->getFunctionScopeIndex(); | 78 | 89.2k | if (ParameterIndex == AttrNonNull.size()) | 79 | 0 | break; | 80 | | | 81 | 89.2k | if (Parameter->hasAttr<NonNullAttr>()) | 82 | 1 | AttrNonNull.set(ParameterIndex); | 83 | 89.2k | } | 84 | 102k | } |
NonNullParamChecker.cpp:void (anonymous namespace)::setBitsAccordingToParameterAttributes<clang::AnyCall>(clang::AnyCall const&, llvm::SmallBitVector&) Line | Count | Source | 75 | 14.0k | llvm::SmallBitVector &AttrNonNull) { | 76 | 14.0k | for (const ParmVarDecl *Parameter : Call.parameters()) { | 77 | 8.16k | unsigned ParameterIndex = Parameter->getFunctionScopeIndex(); | 78 | 8.16k | if (ParameterIndex == AttrNonNull.size()) | 79 | 0 | break; | 80 | | | 81 | 8.16k | if (Parameter->hasAttr<NonNullAttr>()) | 82 | 0 | AttrNonNull.set(ParameterIndex); | 83 | 8.16k | } | 84 | 14.0k | } |
|
85 | | |
86 | | template <class CallType> |
87 | | llvm::SmallBitVector getNonNullAttrsImpl(const CallType &Call, |
88 | 116k | unsigned ExpectedSize) { |
89 | 116k | llvm::SmallBitVector AttrNonNull(ExpectedSize); |
90 | | |
91 | 116k | setBitsAccordingToFunctionAttributes(Call, AttrNonNull); |
92 | 116k | setBitsAccordingToParameterAttributes(Call, AttrNonNull); |
93 | | |
94 | 116k | return AttrNonNull; |
95 | 116k | } NonNullParamChecker.cpp:llvm::SmallBitVector (anonymous namespace)::getNonNullAttrsImpl<clang::ento::CallEvent>(clang::ento::CallEvent const&, unsigned int) Line | Count | Source | 88 | 102k | unsigned ExpectedSize) { | 89 | 102k | llvm::SmallBitVector AttrNonNull(ExpectedSize); | 90 | | | 91 | 102k | setBitsAccordingToFunctionAttributes(Call, AttrNonNull); | 92 | 102k | setBitsAccordingToParameterAttributes(Call, AttrNonNull); | 93 | | | 94 | 102k | return AttrNonNull; | 95 | 102k | } |
NonNullParamChecker.cpp:llvm::SmallBitVector (anonymous namespace)::getNonNullAttrsImpl<clang::AnyCall>(clang::AnyCall const&, unsigned int) Line | Count | Source | 88 | 14.0k | unsigned ExpectedSize) { | 89 | 14.0k | llvm::SmallBitVector AttrNonNull(ExpectedSize); | 90 | | | 91 | 14.0k | setBitsAccordingToFunctionAttributes(Call, AttrNonNull); | 92 | 14.0k | setBitsAccordingToParameterAttributes(Call, AttrNonNull); | 93 | | | 94 | 14.0k | return AttrNonNull; | 95 | 14.0k | } |
|
96 | | |
97 | | /// \return Bitvector marking non-null attributes. |
98 | 102k | llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) { |
99 | 102k | return getNonNullAttrsImpl(Call, Call.getNumArgs()); |
100 | 102k | } |
101 | | |
102 | | /// \return Bitvector marking non-null attributes. |
103 | 14.0k | llvm::SmallBitVector getNonNullAttrs(const AnyCall &Call) { |
104 | 14.0k | return getNonNullAttrsImpl(Call, Call.param_size()); |
105 | 14.0k | } |
106 | | } // end anonymous namespace |
107 | | |
108 | | void NonNullParamChecker::checkPreCall(const CallEvent &Call, |
109 | 102k | CheckerContext &C) const { |
110 | 102k | if (!Call.getDecl()) |
111 | 59 | return; |
112 | | |
113 | 102k | llvm::SmallBitVector AttrNonNull = getNonNullAttrs(Call); |
114 | 102k | unsigned NumArgs = Call.getNumArgs(); |
115 | | |
116 | 102k | ProgramStateRef state = C.getState(); |
117 | 102k | ArrayRef<ParmVarDecl *> parms = Call.parameters(); |
118 | | |
119 | 192k | for (unsigned idx = 0; idx < NumArgs; ++idx89.9k ) { |
120 | | // For vararg functions, a corresponding parameter decl may not exist. |
121 | 89.9k | bool HasParam = idx < parms.size(); |
122 | | |
123 | | // Check if the parameter is a reference. We want to report when reference |
124 | | // to a null pointer is passed as a parameter. |
125 | 89.9k | bool HasRefTypeParam = |
126 | 89.9k | HasParam ? parms[idx]->getType()->isReferenceType()89.2k : false694 ; |
127 | 89.9k | bool ExpectedToBeNonNull = AttrNonNull.test(idx); |
128 | | |
129 | 89.9k | if (!ExpectedToBeNonNull && !HasRefTypeParam89.8k ) |
130 | 56.3k | continue; |
131 | | |
132 | | // If the value is unknown or undefined, we can't perform this check. |
133 | 33.6k | const Expr *ArgE = Call.getArgExpr(idx); |
134 | 33.6k | SVal V = Call.getArgSVal(idx); |
135 | 33.6k | auto DV = V.getAs<DefinedSVal>(); |
136 | 33.6k | if (!DV) |
137 | 28 | continue; |
138 | | |
139 | 33.5k | assert(!HasRefTypeParam || isa<Loc>(*DV)); |
140 | | |
141 | | // Process the case when the argument is not a location. |
142 | 33.5k | if (ExpectedToBeNonNull && !isa<Loc>(*DV)133 ) { |
143 | | // If the argument is a union type, we want to handle a potential |
144 | | // transparent_union GCC extension. |
145 | 24 | if (!ArgE) |
146 | 0 | continue; |
147 | | |
148 | 24 | QualType T = ArgE->getType(); |
149 | 24 | const RecordType *UT = T->getAsUnionType(); |
150 | 24 | if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>()9 ) |
151 | 15 | continue; |
152 | | |
153 | 9 | auto CSV = DV->getAs<nonloc::CompoundVal>(); |
154 | | |
155 | | // FIXME: Handle LazyCompoundVals? |
156 | 9 | if (!CSV) |
157 | 1 | continue; |
158 | | |
159 | 8 | V = *(CSV->begin()); |
160 | 8 | DV = V.getAs<DefinedSVal>(); |
161 | 8 | assert(++CSV->begin() == CSV->end()); |
162 | | // FIXME: Handle (some_union){ some_other_union_val }, which turns into |
163 | | // a LazyCompoundVal inside a CompoundVal. |
164 | 8 | if (!isa<Loc>(V)) |
165 | 1 | continue; |
166 | | |
167 | | // Retrieve the corresponding expression. |
168 | 7 | if (const auto *CE = dyn_cast<CompoundLiteralExpr>(ArgE)) |
169 | 7 | if (const auto *IE = dyn_cast<InitListExpr>(CE->getInitializer())) |
170 | 7 | ArgE = dyn_cast<Expr>(*(IE->begin())); |
171 | 7 | } |
172 | | |
173 | 33.5k | ConstraintManager &CM = C.getConstraintManager(); |
174 | 33.5k | ProgramStateRef stateNotNull, stateNull; |
175 | 33.5k | std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); |
176 | | |
177 | | // Generate an error node. Check for a null node in case |
178 | | // we cache out. |
179 | 33.5k | if (stateNull && !stateNotNull118 ) { |
180 | 49 | if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) { |
181 | | |
182 | 49 | std::unique_ptr<BugReport> R; |
183 | 49 | if (ExpectedToBeNonNull) |
184 | 39 | R = genReportNullAttrNonNull(errorNode, ArgE, idx + 1); |
185 | 10 | else if (HasRefTypeParam) |
186 | 10 | R = genReportReferenceToNullPointer(errorNode, ArgE); |
187 | | |
188 | | // Highlight the range of the argument that was null. |
189 | 49 | R->addRange(Call.getArgSourceRange(idx)); |
190 | | |
191 | | // Emit the bug report. |
192 | 49 | C.emitReport(std::move(R)); |
193 | 49 | } |
194 | | |
195 | | // Always return. Either we cached out or we just emitted an error. |
196 | 49 | return; |
197 | 49 | } |
198 | | |
199 | 33.5k | if (stateNull) { |
200 | 69 | if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) { |
201 | 62 | ImplicitNullDerefEvent event = { |
202 | 62 | V, false, N, &C.getBugReporter(), |
203 | 62 | /*IsDirectDereference=*/HasRefTypeParam}; |
204 | 62 | dispatchEvent(event); |
205 | 62 | } |
206 | 69 | } |
207 | | |
208 | | // If a pointer value passed the check we should assume that it is |
209 | | // indeed not null from this point forward. |
210 | 33.5k | state = stateNotNull; |
211 | 33.5k | } |
212 | | |
213 | | // If we reach here all of the arguments passed the nonnull check. |
214 | | // If 'state' has been updated generated a new node. |
215 | 102k | C.addTransition(state); |
216 | 102k | } |
217 | | |
218 | | /// We want to trust developer annotations and consider all 'nonnull' parameters |
219 | | /// as non-null indeed. Each marked parameter will get a corresponding |
220 | | /// constraint. |
221 | | /// |
222 | | /// This approach will not only help us to get rid of some false positives, but |
223 | | /// remove duplicates and shorten warning traces as well. |
224 | | /// |
225 | | /// \code |
226 | | /// void foo(int *x) [[gnu::nonnull]] { |
227 | | /// // . . . |
228 | | /// *x = 42; // we don't want to consider this as an error... |
229 | | /// // . . . |
230 | | /// } |
231 | | /// |
232 | | /// foo(nullptr); // ...and report here instead |
233 | | /// \endcode |
234 | 48.7k | void NonNullParamChecker::checkBeginFunction(CheckerContext &Context) const { |
235 | | // Planned assumption makes sense only for top-level functions. |
236 | | // Inlined functions will get similar constraints as part of 'checkPreCall'. |
237 | 48.7k | if (!Context.inTopFrame()) |
238 | 34.4k | return; |
239 | | |
240 | 14.3k | const LocationContext *LocContext = Context.getLocationContext(); |
241 | | |
242 | 14.3k | const Decl *FD = LocContext->getDecl(); |
243 | | // AnyCall helps us here to avoid checking for FunctionDecl and ObjCMethodDecl |
244 | | // separately and aggregates interfaces of these classes. |
245 | 14.3k | auto AbstractCall = AnyCall::forDecl(FD); |
246 | 14.3k | if (!AbstractCall) |
247 | 229 | return; |
248 | | |
249 | 14.0k | ProgramStateRef State = Context.getState(); |
250 | 14.0k | llvm::SmallBitVector ParameterNonNullMarks = getNonNullAttrs(*AbstractCall); |
251 | | |
252 | 14.0k | for (const ParmVarDecl *Parameter : AbstractCall->parameters()) { |
253 | | // 1. Check parameter if it is annotated as non-null |
254 | 8.16k | if (!ParameterNonNullMarks.test(Parameter->getFunctionScopeIndex())) |
255 | 8.15k | continue; |
256 | | |
257 | | // 2. Check that parameter is a pointer. |
258 | | // Nonnull attribute can be applied to non-pointers (by default |
259 | | // __attribute__(nonnull) implies "all parameters"). |
260 | 8 | if (!Parameter->getType()->isPointerType()) |
261 | 1 | continue; |
262 | | |
263 | 7 | Loc ParameterLoc = State->getLValue(Parameter, LocContext); |
264 | | // We never consider top-level function parameters undefined. |
265 | 7 | auto StoredVal = |
266 | 7 | State->getSVal(ParameterLoc).castAs<DefinedOrUnknownSVal>(); |
267 | | |
268 | | // 3. Assume that it is indeed non-null |
269 | 7 | if (ProgramStateRef NewState = State->assume(StoredVal, true)) { |
270 | 7 | State = NewState; |
271 | 7 | } |
272 | 7 | } |
273 | | |
274 | 14.0k | Context.addTransition(State); |
275 | 14.0k | } |
276 | | |
277 | | std::unique_ptr<PathSensitiveBugReport> |
278 | | NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode, |
279 | | const Expr *ArgE, |
280 | 39 | unsigned IdxOfArg) const { |
281 | | // Lazily allocate the BugType object if it hasn't already been |
282 | | // created. Ownership is transferred to the BugReporter object once |
283 | | // the BugReport is passed to 'EmitWarning'. |
284 | 39 | if (!BTAttrNonNull) |
285 | 13 | BTAttrNonNull.reset(new BugType( |
286 | 13 | this, "Argument with 'nonnull' attribute passed null", "API")); |
287 | | |
288 | 39 | llvm::SmallString<256> SBuf; |
289 | 39 | llvm::raw_svector_ostream OS(SBuf); |
290 | 39 | OS << "Null pointer passed to " |
291 | 39 | << IdxOfArg << llvm::getOrdinalSuffix(IdxOfArg) |
292 | 39 | << " parameter expecting 'nonnull'"; |
293 | | |
294 | 39 | auto R = |
295 | 39 | std::make_unique<PathSensitiveBugReport>(*BTAttrNonNull, SBuf, ErrorNode); |
296 | 39 | if (ArgE) |
297 | 39 | bugreporter::trackExpressionValue(ErrorNode, ArgE, *R); |
298 | | |
299 | 39 | return R; |
300 | 39 | } |
301 | | |
302 | | std::unique_ptr<PathSensitiveBugReport> |
303 | | NonNullParamChecker::genReportReferenceToNullPointer( |
304 | 10 | const ExplodedNode *ErrorNode, const Expr *ArgE) const { |
305 | 10 | if (!BTNullRefArg) |
306 | 6 | BTNullRefArg.reset(new BugType(this, "Dereference of null pointer")); |
307 | | |
308 | 10 | auto R = std::make_unique<PathSensitiveBugReport>( |
309 | 10 | *BTNullRefArg, "Forming reference to null pointer", ErrorNode); |
310 | 10 | if (ArgE) { |
311 | 10 | const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE); |
312 | 10 | if (!ArgEDeref) |
313 | 0 | ArgEDeref = ArgE; |
314 | 10 | bugreporter::trackExpressionValue(ErrorNode, ArgEDeref, *R); |
315 | 10 | } |
316 | 10 | return R; |
317 | | |
318 | 10 | } |
319 | | |
320 | 1.27k | void ento::registerNonNullParamChecker(CheckerManager &mgr) { |
321 | 1.27k | mgr.registerChecker<NonNullParamChecker>(); |
322 | 1.27k | } |
323 | | |
324 | 2.55k | bool ento::shouldRegisterNonNullParamChecker(const CheckerManager &mgr) { |
325 | 2.55k | return true; |
326 | 2.55k | } |