1 | //===--- CallAndMessageChecker.cpp ------------------------------*- 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 CallAndMessageChecker, a builtin checker that checks for various |
10 | // errors of call and objc message expressions. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "clang/AST/ExprCXX.h" |
15 | #include "clang/AST/ParentMap.h" |
16 | #include "clang/Basic/TargetInfo.h" |
17 | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
18 | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
19 | #include "clang/StaticAnalyzer/Core/Checker.h" |
20 | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
21 | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
22 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
23 | #include "llvm/ADT/SmallString.h" |
24 | #include "llvm/ADT/StringExtras.h" |
25 | #include "llvm/Support/Casting.h" |
26 | #include "llvm/Support/raw_ostream.h" |
27 | |
28 | using namespace clang; |
29 | using namespace ento; |
30 | |
31 | namespace { |
32 | |
33 | class CallAndMessageChecker |
34 | : public Checker<check::PreObjCMessage, check::ObjCMessageNil, |
35 | check::PreCall> { |
36 | mutable std::unique_ptr<BugType> BT_call_null; |
37 | mutable std::unique_ptr<BugType> BT_call_undef; |
38 | mutable std::unique_ptr<BugType> BT_cxx_call_null; |
39 | mutable std::unique_ptr<BugType> BT_cxx_call_undef; |
40 | mutable std::unique_ptr<BugType> BT_call_arg; |
41 | mutable std::unique_ptr<BugType> BT_cxx_delete_undef; |
42 | mutable std::unique_ptr<BugType> BT_msg_undef; |
43 | mutable std::unique_ptr<BugType> BT_objc_prop_undef; |
44 | mutable std::unique_ptr<BugType> BT_objc_subscript_undef; |
45 | mutable std::unique_ptr<BugType> BT_msg_arg; |
46 | mutable std::unique_ptr<BugType> BT_msg_ret; |
47 | mutable std::unique_ptr<BugType> BT_call_few_args; |
48 | |
49 | public: |
50 | // These correspond with the checker options. Looking at other checkers such |
51 | // as MallocChecker and CStringChecker, this is similar as to how they pull |
52 | // off having a modeling class, but emitting diagnostics under a smaller |
53 | // checker's name that can be safely disabled without disturbing the |
54 | // underlaying modeling engine. |
55 | // The reason behind having *checker options* rather then actual *checkers* |
56 | // here is that CallAndMessage is among the oldest checkers out there, and can |
57 | // be responsible for the majority of the reports on any given project. This |
58 | // is obviously not ideal, but changing checker name has the consequence of |
59 | // changing the issue hashes associated with the reports, and databases |
60 | // relying on this (CodeChecker, for instance) would suffer greatly. |
61 | // If we ever end up making changes to the issue hash generation algorithm, or |
62 | // the warning messages here, we should totally jump on the opportunity to |
63 | // convert these to actual checkers. |
64 | enum CheckKind { |
65 | CK_FunctionPointer, |
66 | CK_ParameterCount, |
67 | CK_CXXThisMethodCall, |
68 | CK_CXXDeallocationArg, |
69 | CK_ArgInitializedness, |
70 | CK_ArgPointeeInitializedness, |
71 | CK_NilReceiver, |
72 | CK_UndefReceiver, |
73 | CK_NumCheckKinds |
74 | }; |
75 | |
76 | bool ChecksEnabled[CK_NumCheckKinds] = {false}; |
77 | // The original core.CallAndMessage checker name. This should rather be an |
78 | // array, as seen in MallocChecker and CStringChecker. |
79 | CheckerNameRef OriginalName; |
80 | |
81 | void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; |
82 | |
83 | /// Fill in the return value that results from messaging nil based on the |
84 | /// return type and architecture and diagnose if the return value will be |
85 | /// garbage. |
86 | void checkObjCMessageNil(const ObjCMethodCall &msg, CheckerContext &C) const; |
87 | |
88 | void checkPreCall(const CallEvent &Call, CheckerContext &C) const; |
89 | |
90 | ProgramStateRef checkFunctionPointerCall(const CallExpr *CE, |
91 | CheckerContext &C, |
92 | ProgramStateRef State) const; |
93 | |
94 | ProgramStateRef checkCXXMethodCall(const CXXInstanceCall *CC, |
95 | CheckerContext &C, |
96 | ProgramStateRef State) const; |
97 | |
98 | ProgramStateRef checkParameterCount(const CallEvent &Call, CheckerContext &C, |
99 | ProgramStateRef State) const; |
100 | |
101 | ProgramStateRef checkCXXDeallocation(const CXXDeallocatorCall *DC, |
102 | CheckerContext &C, |
103 | ProgramStateRef State) const; |
104 | |
105 | ProgramStateRef checkArgInitializedness(const CallEvent &Call, |
106 | CheckerContext &C, |
107 | ProgramStateRef State) const; |
108 | |
109 | private: |
110 | bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange ArgRange, |
111 | const Expr *ArgEx, int ArgumentNumber, |
112 | bool CheckUninitFields, const CallEvent &Call, |
113 | std::unique_ptr<BugType> &BT, |
114 | const ParmVarDecl *ParamDecl) const; |
115 | |
116 | static void emitBadCall(BugType *BT, CheckerContext &C, const Expr *BadE); |
117 | void emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg, |
118 | ExplodedNode *N) const; |
119 | |
120 | void HandleNilReceiver(CheckerContext &C, |
121 | ProgramStateRef state, |
122 | const ObjCMethodCall &msg) const; |
123 | |
124 | void LazyInit_BT(const char *desc, std::unique_ptr<BugType> &BT) const { |
125 | if (!BT) |
126 | BT.reset(p: new BugType(OriginalName, desc)); |
127 | } |
128 | bool uninitRefOrPointer(CheckerContext &C, SVal V, SourceRange ArgRange, |
129 | const Expr *ArgEx, std::unique_ptr<BugType> &BT, |
130 | const ParmVarDecl *ParamDecl, const char *BD, |
131 | int ArgumentNumber) const; |
132 | }; |
133 | } // end anonymous namespace |
134 | |
135 | void CallAndMessageChecker::emitBadCall(BugType *BT, CheckerContext &C, |
136 | const Expr *BadE) { |
137 | ExplodedNode *N = C.generateErrorNode(); |
138 | if (!N) |
139 | return; |
140 | |
141 | auto R = std::make_unique<PathSensitiveBugReport>(args&: *BT, args: BT->getDescription(), args&: N); |
142 | if (BadE) { |
143 | R->addRange(R: BadE->getSourceRange()); |
144 | if (BadE->isGLValue()) |
145 | BadE = bugreporter::getDerefExpr(BadE); |
146 | bugreporter::trackExpressionValue(N, E: BadE, R&: *R); |
147 | } |
148 | C.emitReport(R: std::move(R)); |
149 | } |
150 | |
151 | static void describeUninitializedArgumentInCall(const CallEvent &Call, |
152 | int ArgumentNumber, |
153 | llvm::raw_svector_ostream &Os) { |
154 | switch (Call.getKind()) { |
155 | case CE_ObjCMessage: { |
156 | const ObjCMethodCall &Msg = cast<ObjCMethodCall>(Val: Call); |
157 | switch (Msg.getMessageKind()) { |
158 | case OCM_Message: |
159 | Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(Val: ArgumentNumber + 1) |
160 | << " argument in message expression is an uninitialized value" ; |
161 | return; |
162 | case OCM_PropertyAccess: |
163 | assert(Msg.isSetter() && "Getters have no args" ); |
164 | Os << "Argument for property setter is an uninitialized value" ; |
165 | return; |
166 | case OCM_Subscript: |
167 | if (Msg.isSetter() && (ArgumentNumber == 0)) |
168 | Os << "Argument for subscript setter is an uninitialized value" ; |
169 | else |
170 | Os << "Subscript index is an uninitialized value" ; |
171 | return; |
172 | } |
173 | llvm_unreachable("Unknown message kind." ); |
174 | } |
175 | case CE_Block: |
176 | Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(Val: ArgumentNumber + 1) |
177 | << " block call argument is an uninitialized value" ; |
178 | return; |
179 | default: |
180 | Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(Val: ArgumentNumber + 1) |
181 | << " function call argument is an uninitialized value" ; |
182 | return; |
183 | } |
184 | } |
185 | |
186 | bool CallAndMessageChecker::uninitRefOrPointer( |
187 | CheckerContext &C, SVal V, SourceRange ArgRange, const Expr *ArgEx, |
188 | std::unique_ptr<BugType> &BT, const ParmVarDecl *ParamDecl, const char *BD, |
189 | int ArgumentNumber) const { |
190 | |
191 | // The pointee being uninitialized is a sign of code smell, not a bug, no need |
192 | // to sink here. |
193 | if (!ChecksEnabled[CK_ArgPointeeInitializedness]) |
194 | return false; |
195 | |
196 | // No parameter declaration available, i.e. variadic function argument. |
197 | if(!ParamDecl) |
198 | return false; |
199 | |
200 | // If parameter is declared as pointer to const in function declaration, |
201 | // then check if corresponding argument in function call is |
202 | // pointing to undefined symbol value (uninitialized memory). |
203 | SmallString<200> Buf; |
204 | llvm::raw_svector_ostream Os(Buf); |
205 | |
206 | if (ParamDecl->getType()->isPointerType()) { |
207 | Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(Val: ArgumentNumber + 1) |
208 | << " function call argument is a pointer to uninitialized value" ; |
209 | } else if (ParamDecl->getType()->isReferenceType()) { |
210 | Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(Val: ArgumentNumber + 1) |
211 | << " function call argument is an uninitialized value" ; |
212 | } else |
213 | return false; |
214 | |
215 | if(!ParamDecl->getType()->getPointeeType().isConstQualified()) |
216 | return false; |
217 | |
218 | if (const MemRegion *SValMemRegion = V.getAsRegion()) { |
219 | const ProgramStateRef State = C.getState(); |
220 | const SVal PSV = State->getSVal(SValMemRegion, C.getASTContext().CharTy); |
221 | if (PSV.isUndef()) { |
222 | if (ExplodedNode *N = C.generateErrorNode()) { |
223 | LazyInit_BT(desc: BD, BT); |
224 | auto R = std::make_unique<PathSensitiveBugReport>(args&: *BT, args: Os.str(), args&: N); |
225 | R->addRange(R: ArgRange); |
226 | if (ArgEx) |
227 | bugreporter::trackExpressionValue(N, E: ArgEx, R&: *R); |
228 | |
229 | C.emitReport(R: std::move(R)); |
230 | } |
231 | return true; |
232 | } |
233 | } |
234 | return false; |
235 | } |
236 | |
237 | namespace { |
238 | class FindUninitializedField { |
239 | public: |
240 | SmallVector<const FieldDecl *, 10> FieldChain; |
241 | |
242 | private: |
243 | StoreManager &StoreMgr; |
244 | MemRegionManager &MrMgr; |
245 | Store store; |
246 | |
247 | public: |
248 | FindUninitializedField(StoreManager &storeMgr, MemRegionManager &mrMgr, |
249 | Store s) |
250 | : StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {} |
251 | |
252 | bool Find(const TypedValueRegion *R) { |
253 | QualType T = R->getValueType(); |
254 | if (const RecordType *RT = T->getAsStructureType()) { |
255 | const RecordDecl *RD = RT->getDecl()->getDefinition(); |
256 | assert(RD && "Referred record has no definition" ); |
257 | for (const auto *I : RD->fields()) { |
258 | const FieldRegion *FR = MrMgr.getFieldRegion(I, R); |
259 | FieldChain.push_back(I); |
260 | T = I->getType(); |
261 | if (T->getAsStructureType()) { |
262 | if (Find(FR)) |
263 | return true; |
264 | } else { |
265 | SVal V = StoreMgr.getBinding(store, loc::MemRegionVal(FR)); |
266 | if (V.isUndef()) |
267 | return true; |
268 | } |
269 | FieldChain.pop_back(); |
270 | } |
271 | } |
272 | |
273 | return false; |
274 | } |
275 | }; |
276 | } // namespace |
277 | |
278 | bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, |
279 | SVal V, |
280 | SourceRange ArgRange, |
281 | const Expr *ArgEx, |
282 | int ArgumentNumber, |
283 | bool CheckUninitFields, |
284 | const CallEvent &Call, |
285 | std::unique_ptr<BugType> &BT, |
286 | const ParmVarDecl *ParamDecl |
287 | ) const { |
288 | const char *BD = "Uninitialized argument value" ; |
289 | |
290 | if (uninitRefOrPointer(C, V, ArgRange, ArgEx, BT, ParamDecl, BD, |
291 | ArgumentNumber)) |
292 | return true; |
293 | |
294 | if (V.isUndef()) { |
295 | if (!ChecksEnabled[CK_ArgInitializedness]) { |
296 | C.addSink(); |
297 | return true; |
298 | } |
299 | if (ExplodedNode *N = C.generateErrorNode()) { |
300 | LazyInit_BT(desc: BD, BT); |
301 | // Generate a report for this bug. |
302 | SmallString<200> Buf; |
303 | llvm::raw_svector_ostream Os(Buf); |
304 | describeUninitializedArgumentInCall(Call, ArgumentNumber, Os); |
305 | auto R = std::make_unique<PathSensitiveBugReport>(args&: *BT, args: Os.str(), args&: N); |
306 | |
307 | R->addRange(R: ArgRange); |
308 | if (ArgEx) |
309 | bugreporter::trackExpressionValue(N, E: ArgEx, R&: *R); |
310 | C.emitReport(R: std::move(R)); |
311 | } |
312 | return true; |
313 | } |
314 | |
315 | if (!CheckUninitFields) |
316 | return false; |
317 | |
318 | if (auto LV = V.getAs<nonloc::LazyCompoundVal>()) { |
319 | const LazyCompoundValData *D = LV->getCVData(); |
320 | FindUninitializedField F(C.getState()->getStateManager().getStoreManager(), |
321 | C.getSValBuilder().getRegionManager(), |
322 | D->getStore()); |
323 | |
324 | if (F.Find(R: D->getRegion())) { |
325 | if (!ChecksEnabled[CK_ArgInitializedness]) { |
326 | C.addSink(); |
327 | return true; |
328 | } |
329 | if (ExplodedNode *N = C.generateErrorNode()) { |
330 | LazyInit_BT(desc: BD, BT); |
331 | SmallString<512> Str; |
332 | llvm::raw_svector_ostream os(Str); |
333 | os << "Passed-by-value struct argument contains uninitialized data" ; |
334 | |
335 | if (F.FieldChain.size() == 1) |
336 | os << " (e.g., field: '" << *F.FieldChain[0] << "')" ; |
337 | else { |
338 | os << " (e.g., via the field chain: '" ; |
339 | bool first = true; |
340 | for (SmallVectorImpl<const FieldDecl *>::iterator |
341 | DI = F.FieldChain.begin(), DE = F.FieldChain.end(); DI!=DE;++DI){ |
342 | if (first) |
343 | first = false; |
344 | else |
345 | os << '.'; |
346 | os << **DI; |
347 | } |
348 | os << "')" ; |
349 | } |
350 | |
351 | // Generate a report for this bug. |
352 | auto R = std::make_unique<PathSensitiveBugReport>(args&: *BT, args: os.str(), args&: N); |
353 | R->addRange(R: ArgRange); |
354 | |
355 | if (ArgEx) |
356 | bugreporter::trackExpressionValue(N, E: ArgEx, R&: *R); |
357 | // FIXME: enhance track back for uninitialized value for arbitrary |
358 | // memregions |
359 | C.emitReport(R: std::move(R)); |
360 | } |
361 | return true; |
362 | } |
363 | } |
364 | |
365 | return false; |
366 | } |
367 | |
368 | ProgramStateRef CallAndMessageChecker::checkFunctionPointerCall( |
369 | const CallExpr *CE, CheckerContext &C, ProgramStateRef State) const { |
370 | |
371 | const Expr *Callee = CE->getCallee()->IgnoreParens(); |
372 | const LocationContext *LCtx = C.getLocationContext(); |
373 | SVal L = State->getSVal(Callee, LCtx); |
374 | |
375 | if (L.isUndef()) { |
376 | if (!ChecksEnabled[CK_FunctionPointer]) { |
377 | C.addSink(State); |
378 | return nullptr; |
379 | } |
380 | if (!BT_call_undef) |
381 | BT_call_undef.reset(p: new BugType( |
382 | OriginalName, |
383 | "Called function pointer is an uninitialized pointer value" )); |
384 | emitBadCall(BT: BT_call_undef.get(), C, BadE: Callee); |
385 | return nullptr; |
386 | } |
387 | |
388 | ProgramStateRef StNonNull, StNull; |
389 | std::tie(args&: StNonNull, args&: StNull) = State->assume(Cond: L.castAs<DefinedOrUnknownSVal>()); |
390 | |
391 | if (StNull && !StNonNull) { |
392 | if (!ChecksEnabled[CK_FunctionPointer]) { |
393 | C.addSink(State: StNull); |
394 | return nullptr; |
395 | } |
396 | if (!BT_call_null) |
397 | BT_call_null.reset(p: new BugType( |
398 | OriginalName, "Called function pointer is null (null dereference)" )); |
399 | emitBadCall(BT: BT_call_null.get(), C, BadE: Callee); |
400 | return nullptr; |
401 | } |
402 | |
403 | return StNonNull; |
404 | } |
405 | |
406 | ProgramStateRef CallAndMessageChecker::checkParameterCount( |
407 | const CallEvent &Call, CheckerContext &C, ProgramStateRef State) const { |
408 | |
409 | // If we have a function or block declaration, we can make sure we pass |
410 | // enough parameters. |
411 | unsigned Params = Call.parameters().size(); |
412 | if (Call.getNumArgs() >= Params) |
413 | return State; |
414 | |
415 | if (!ChecksEnabled[CK_ParameterCount]) { |
416 | C.addSink(State); |
417 | return nullptr; |
418 | } |
419 | |
420 | ExplodedNode *N = C.generateErrorNode(); |
421 | if (!N) |
422 | return nullptr; |
423 | |
424 | LazyInit_BT(desc: "Function call with too few arguments" , BT&: BT_call_few_args); |
425 | |
426 | SmallString<512> Str; |
427 | llvm::raw_svector_ostream os(Str); |
428 | if (isa<AnyFunctionCall>(Val: Call)) { |
429 | os << "Function " ; |
430 | } else { |
431 | assert(isa<BlockCall>(Call)); |
432 | os << "Block " ; |
433 | } |
434 | os << "taking " << Params << " argument" << (Params == 1 ? "" : "s" ) |
435 | << " is called with fewer (" << Call.getNumArgs() << ")" ; |
436 | |
437 | C.emitReport( |
438 | R: std::make_unique<PathSensitiveBugReport>(args&: *BT_call_few_args, args: os.str(), args&: N)); |
439 | return nullptr; |
440 | } |
441 | |
442 | ProgramStateRef CallAndMessageChecker::checkCXXMethodCall( |
443 | const CXXInstanceCall *CC, CheckerContext &C, ProgramStateRef State) const { |
444 | |
445 | SVal V = CC->getCXXThisVal(); |
446 | if (V.isUndef()) { |
447 | if (!ChecksEnabled[CK_CXXThisMethodCall]) { |
448 | C.addSink(State); |
449 | return nullptr; |
450 | } |
451 | if (!BT_cxx_call_undef) |
452 | BT_cxx_call_undef.reset(p: new BugType( |
453 | OriginalName, "Called C++ object pointer is uninitialized" )); |
454 | emitBadCall(BT: BT_cxx_call_undef.get(), C, BadE: CC->getCXXThisExpr()); |
455 | return nullptr; |
456 | } |
457 | |
458 | ProgramStateRef StNonNull, StNull; |
459 | std::tie(args&: StNonNull, args&: StNull) = State->assume(Cond: V.castAs<DefinedOrUnknownSVal>()); |
460 | |
461 | if (StNull && !StNonNull) { |
462 | if (!ChecksEnabled[CK_CXXThisMethodCall]) { |
463 | C.addSink(State: StNull); |
464 | return nullptr; |
465 | } |
466 | if (!BT_cxx_call_null) |
467 | BT_cxx_call_null.reset( |
468 | p: new BugType(OriginalName, "Called C++ object pointer is null" )); |
469 | emitBadCall(BT: BT_cxx_call_null.get(), C, BadE: CC->getCXXThisExpr()); |
470 | return nullptr; |
471 | } |
472 | |
473 | return StNonNull; |
474 | } |
475 | |
476 | ProgramStateRef |
477 | CallAndMessageChecker::checkCXXDeallocation(const CXXDeallocatorCall *DC, |
478 | CheckerContext &C, |
479 | ProgramStateRef State) const { |
480 | const CXXDeleteExpr *DE = DC->getOriginExpr(); |
481 | assert(DE); |
482 | SVal Arg = C.getSVal(DE->getArgument()); |
483 | if (!Arg.isUndef()) |
484 | return State; |
485 | |
486 | if (!ChecksEnabled[CK_CXXDeallocationArg]) { |
487 | C.addSink(State); |
488 | return nullptr; |
489 | } |
490 | |
491 | StringRef Desc; |
492 | ExplodedNode *N = C.generateErrorNode(); |
493 | if (!N) |
494 | return nullptr; |
495 | if (!BT_cxx_delete_undef) |
496 | BT_cxx_delete_undef.reset( |
497 | p: new BugType(OriginalName, "Uninitialized argument value" )); |
498 | if (DE->isArrayFormAsWritten()) |
499 | Desc = "Argument to 'delete[]' is uninitialized" ; |
500 | else |
501 | Desc = "Argument to 'delete' is uninitialized" ; |
502 | auto R = |
503 | std::make_unique<PathSensitiveBugReport>(args&: *BT_cxx_delete_undef, args&: Desc, args&: N); |
504 | bugreporter::trackExpressionValue(N, DE, *R); |
505 | C.emitReport(R: std::move(R)); |
506 | return nullptr; |
507 | } |
508 | |
509 | ProgramStateRef CallAndMessageChecker::checkArgInitializedness( |
510 | const CallEvent &Call, CheckerContext &C, ProgramStateRef State) const { |
511 | |
512 | const Decl *D = Call.getDecl(); |
513 | |
514 | // Don't check for uninitialized field values in arguments if the |
515 | // caller has a body that is available and we have the chance to inline it. |
516 | // This is a hack, but is a reasonable compromise betweens sometimes warning |
517 | // and sometimes not depending on if we decide to inline a function. |
518 | const bool checkUninitFields = |
519 | !(C.getAnalysisManager().shouldInlineCall() && (D && D->getBody())); |
520 | |
521 | std::unique_ptr<BugType> *BT; |
522 | if (isa<ObjCMethodCall>(Val: Call)) |
523 | BT = &BT_msg_arg; |
524 | else |
525 | BT = &BT_call_arg; |
526 | |
527 | const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Val: D); |
528 | for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i) { |
529 | const ParmVarDecl *ParamDecl = nullptr; |
530 | if (FD && i < FD->getNumParams()) |
531 | ParamDecl = FD->getParamDecl(i); |
532 | if (PreVisitProcessArg(C, V: Call.getArgSVal(Index: i), ArgRange: Call.getArgSourceRange(Index: i), |
533 | ArgEx: Call.getArgExpr(Index: i), ArgumentNumber: i, CheckUninitFields: checkUninitFields, Call, BT&: *BT, |
534 | ParamDecl)) |
535 | return nullptr; |
536 | } |
537 | return State; |
538 | } |
539 | |
540 | void CallAndMessageChecker::checkPreCall(const CallEvent &Call, |
541 | CheckerContext &C) const { |
542 | ProgramStateRef State = C.getState(); |
543 | |
544 | if (const CallExpr *CE = dyn_cast_or_null<CallExpr>(Val: Call.getOriginExpr())) |
545 | State = checkFunctionPointerCall(CE, C, State); |
546 | |
547 | if (!State) |
548 | return; |
549 | |
550 | if (Call.getDecl()) |
551 | State = checkParameterCount(Call, C, State); |
552 | |
553 | if (!State) |
554 | return; |
555 | |
556 | if (const auto *CC = dyn_cast<CXXInstanceCall>(Val: &Call)) |
557 | State = checkCXXMethodCall(CC, C, State); |
558 | |
559 | if (!State) |
560 | return; |
561 | |
562 | if (const auto *DC = dyn_cast<CXXDeallocatorCall>(Val: &Call)) |
563 | State = checkCXXDeallocation(DC, C, State); |
564 | |
565 | if (!State) |
566 | return; |
567 | |
568 | State = checkArgInitializedness(Call, C, State); |
569 | |
570 | // If we make it here, record our assumptions about the callee. |
571 | C.addTransition(State); |
572 | } |
573 | |
574 | void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg, |
575 | CheckerContext &C) const { |
576 | SVal recVal = msg.getReceiverSVal(); |
577 | if (recVal.isUndef()) { |
578 | if (!ChecksEnabled[CK_UndefReceiver]) { |
579 | C.addSink(); |
580 | return; |
581 | } |
582 | if (ExplodedNode *N = C.generateErrorNode()) { |
583 | BugType *BT = nullptr; |
584 | switch (msg.getMessageKind()) { |
585 | case OCM_Message: |
586 | if (!BT_msg_undef) |
587 | BT_msg_undef.reset(p: new BugType(OriginalName, |
588 | "Receiver in message expression " |
589 | "is an uninitialized value" )); |
590 | BT = BT_msg_undef.get(); |
591 | break; |
592 | case OCM_PropertyAccess: |
593 | if (!BT_objc_prop_undef) |
594 | BT_objc_prop_undef.reset(p: new BugType( |
595 | OriginalName, |
596 | "Property access on an uninitialized object pointer" )); |
597 | BT = BT_objc_prop_undef.get(); |
598 | break; |
599 | case OCM_Subscript: |
600 | if (!BT_objc_subscript_undef) |
601 | BT_objc_subscript_undef.reset(p: new BugType( |
602 | OriginalName, |
603 | "Subscript access on an uninitialized object pointer" )); |
604 | BT = BT_objc_subscript_undef.get(); |
605 | break; |
606 | } |
607 | assert(BT && "Unknown message kind." ); |
608 | |
609 | auto R = std::make_unique<PathSensitiveBugReport>(args&: *BT, args: BT->getDescription(), args&: N); |
610 | const ObjCMessageExpr *ME = msg.getOriginExpr(); |
611 | R->addRange(R: ME->getReceiverRange()); |
612 | |
613 | // FIXME: getTrackNullOrUndefValueVisitor can't handle "super" yet. |
614 | if (const Expr *ReceiverE = ME->getInstanceReceiver()) |
615 | bugreporter::trackExpressionValue(N, E: ReceiverE, R&: *R); |
616 | C.emitReport(R: std::move(R)); |
617 | } |
618 | return; |
619 | } |
620 | } |
621 | |
622 | void CallAndMessageChecker::checkObjCMessageNil(const ObjCMethodCall &msg, |
623 | CheckerContext &C) const { |
624 | HandleNilReceiver(C, state: C.getState(), msg); |
625 | } |
626 | |
627 | void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, |
628 | const ObjCMethodCall &msg, |
629 | ExplodedNode *N) const { |
630 | if (!ChecksEnabled[CK_NilReceiver]) { |
631 | C.addSink(); |
632 | return; |
633 | } |
634 | |
635 | if (!BT_msg_ret) |
636 | BT_msg_ret.reset( |
637 | p: new BugType(OriginalName, "Receiver in message expression is 'nil'" )); |
638 | |
639 | const ObjCMessageExpr *ME = msg.getOriginExpr(); |
640 | |
641 | QualType ResTy = msg.getResultType(); |
642 | |
643 | SmallString<200> buf; |
644 | llvm::raw_svector_ostream os(buf); |
645 | os << "The receiver of message '" ; |
646 | ME->getSelector().print(OS&: os); |
647 | os << "' is nil" ; |
648 | if (ResTy->isReferenceType()) { |
649 | os << ", which results in forming a null reference" ; |
650 | } else { |
651 | os << " and returns a value of type '" ; |
652 | msg.getResultType().print(OS&: os, Policy: C.getLangOpts()); |
653 | os << "' that will be garbage" ; |
654 | } |
655 | |
656 | auto report = |
657 | std::make_unique<PathSensitiveBugReport>(args&: *BT_msg_ret, args: os.str(), args&: N); |
658 | report->addRange(R: ME->getReceiverRange()); |
659 | // FIXME: This won't track "self" in messages to super. |
660 | if (const Expr *receiver = ME->getInstanceReceiver()) { |
661 | bugreporter::trackExpressionValue(N, E: receiver, R&: *report); |
662 | } |
663 | C.emitReport(R: std::move(report)); |
664 | } |
665 | |
666 | static bool supportsNilWithFloatRet(const llvm::Triple &triple) { |
667 | return (triple.getVendor() == llvm::Triple::Apple && |
668 | (triple.isiOS() || triple.isWatchOS() || |
669 | !triple.isMacOSXVersionLT(Major: 10,Minor: 5))); |
670 | } |
671 | |
672 | void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, |
673 | ProgramStateRef state, |
674 | const ObjCMethodCall &Msg) const { |
675 | ASTContext &Ctx = C.getASTContext(); |
676 | static CheckerProgramPointTag Tag(this, "NilReceiver" ); |
677 | |
678 | // Check the return type of the message expression. A message to nil will |
679 | // return different values depending on the return type and the architecture. |
680 | QualType RetTy = Msg.getResultType(); |
681 | CanQualType CanRetTy = Ctx.getCanonicalType(T: RetTy); |
682 | const LocationContext *LCtx = C.getLocationContext(); |
683 | |
684 | if (CanRetTy->isStructureOrClassType()) { |
685 | // Structure returns are safe since the compiler zeroes them out. |
686 | SVal V = C.getSValBuilder().makeZeroVal(type: RetTy); |
687 | C.addTransition(State: state->BindExpr(Msg.getOriginExpr(), LCtx, V), Tag: &Tag); |
688 | return; |
689 | } |
690 | |
691 | // Other cases: check if sizeof(return type) > sizeof(void*) |
692 | if (CanRetTy != Ctx.VoidTy && C.getLocationContext()->getParentMap() |
693 | .isConsumedExpr(Msg.getOriginExpr())) { |
694 | // Compute: sizeof(void *) and sizeof(return type) |
695 | const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy); |
696 | const uint64_t returnTypeSize = Ctx.getTypeSize(T: CanRetTy); |
697 | |
698 | if (CanRetTy.getTypePtr()->isReferenceType()|| |
699 | (voidPtrSize < returnTypeSize && |
700 | !(supportsNilWithFloatRet(triple: Ctx.getTargetInfo().getTriple()) && |
701 | (Ctx.FloatTy == CanRetTy || |
702 | Ctx.DoubleTy == CanRetTy || |
703 | Ctx.LongDoubleTy == CanRetTy || |
704 | Ctx.LongLongTy == CanRetTy || |
705 | Ctx.UnsignedLongLongTy == CanRetTy)))) { |
706 | if (ExplodedNode *N = C.generateErrorNode(State: state, Tag: &Tag)) |
707 | emitNilReceiverBug(C, msg: Msg, N); |
708 | return; |
709 | } |
710 | |
711 | // Handle the safe cases where the return value is 0 if the |
712 | // receiver is nil. |
713 | // |
714 | // FIXME: For now take the conservative approach that we only |
715 | // return null values if we *know* that the receiver is nil. |
716 | // This is because we can have surprises like: |
717 | // |
718 | // ... = [[NSScreens screens] objectAtIndex:0]; |
719 | // |
720 | // What can happen is that [... screens] could return nil, but |
721 | // it most likely isn't nil. We should assume the semantics |
722 | // of this case unless we have *a lot* more knowledge. |
723 | // |
724 | SVal V = C.getSValBuilder().makeZeroVal(type: RetTy); |
725 | C.addTransition(State: state->BindExpr(Msg.getOriginExpr(), LCtx, V), Tag: &Tag); |
726 | return; |
727 | } |
728 | |
729 | C.addTransition(State: state); |
730 | } |
731 | |
732 | void ento::registerCallAndMessageModeling(CheckerManager &mgr) { |
733 | mgr.registerChecker<CallAndMessageChecker>(); |
734 | } |
735 | |
736 | bool ento::shouldRegisterCallAndMessageModeling(const CheckerManager &mgr) { |
737 | return true; |
738 | } |
739 | |
740 | void ento::registerCallAndMessageChecker(CheckerManager &mgr) { |
741 | CallAndMessageChecker *checker = mgr.getChecker<CallAndMessageChecker>(); |
742 | |
743 | checker->OriginalName = mgr.getCurrentCheckerName(); |
744 | |
745 | #define QUERY_CHECKER_OPTION(OPTION) \ |
746 | checker->ChecksEnabled[CallAndMessageChecker::CK_##OPTION] = \ |
747 | mgr.getAnalyzerOptions().getCheckerBooleanOption( \ |
748 | mgr.getCurrentCheckerName(), #OPTION); |
749 | |
750 | QUERY_CHECKER_OPTION(FunctionPointer) |
751 | QUERY_CHECKER_OPTION(ParameterCount) |
752 | QUERY_CHECKER_OPTION(CXXThisMethodCall) |
753 | QUERY_CHECKER_OPTION(CXXDeallocationArg) |
754 | QUERY_CHECKER_OPTION(ArgInitializedness) |
755 | QUERY_CHECKER_OPTION(ArgPointeeInitializedness) |
756 | QUERY_CHECKER_OPTION(NilReceiver) |
757 | QUERY_CHECKER_OPTION(UndefReceiver) |
758 | } |
759 | |
760 | bool ento::shouldRegisterCallAndMessageChecker(const CheckerManager &mgr) { |
761 | return true; |
762 | } |
763 | |