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/StringExtras.h" |
24 | #include "llvm/Support/raw_ostream.h" |
25 | |
26 | using namespace clang; |
27 | using namespace ento; |
28 | |
29 | namespace { |
30 | |
31 | class CallAndMessageChecker |
32 | : public Checker<check::PreObjCMessage, check::ObjCMessageNil, |
33 | check::PreCall> { |
34 | mutable std::unique_ptr<BugType> BT_call_null; |
35 | mutable std::unique_ptr<BugType> BT_call_undef; |
36 | mutable std::unique_ptr<BugType> BT_cxx_call_null; |
37 | mutable std::unique_ptr<BugType> BT_cxx_call_undef; |
38 | mutable std::unique_ptr<BugType> BT_call_arg; |
39 | mutable std::unique_ptr<BugType> BT_cxx_delete_undef; |
40 | mutable std::unique_ptr<BugType> BT_msg_undef; |
41 | mutable std::unique_ptr<BugType> BT_objc_prop_undef; |
42 | mutable std::unique_ptr<BugType> BT_objc_subscript_undef; |
43 | mutable std::unique_ptr<BugType> BT_msg_arg; |
44 | mutable std::unique_ptr<BugType> BT_msg_ret; |
45 | mutable std::unique_ptr<BugType> BT_call_few_args; |
46 | |
47 | public: |
48 | // These correspond with the checker options. Looking at other checkers such |
49 | // as MallocChecker and CStringChecker, this is similar as to how they pull |
50 | // off having a modeling class, but emitting diagnostics under a smaller |
51 | // checker's name that can be safely disabled without disturbing the |
52 | // underlaying modeling engine. |
53 | // The reason behind having *checker options* rather then actual *checkers* |
54 | // here is that CallAndMessage is among the oldest checkers out there, and can |
55 | // be responsible for the majority of the reports on any given project. This |
56 | // is obviously not ideal, but changing checker name has the consequence of |
57 | // changing the issue hashes associated with the reports, and databases |
58 | // relying on this (CodeChecker, for instance) would suffer greatly. |
59 | // If we ever end up making changes to the issue hash generation algorithm, or |
60 | // the warning messages here, we should totally jump on the opportunity to |
61 | // convert these to actual checkers. |
62 | enum CheckKind { |
63 | CK_FunctionPointer, |
64 | CK_ParameterCount, |
65 | CK_CXXThisMethodCall, |
66 | CK_CXXDeallocationArg, |
67 | CK_ArgInitializedness, |
68 | CK_ArgPointeeInitializedness, |
69 | CK_NilReceiver, |
70 | CK_UndefReceiver, |
71 | CK_NumCheckKinds |
72 | }; |
73 | |
74 | bool ChecksEnabled[CK_NumCheckKinds] = {false}; |
75 | // The original core.CallAndMessage checker name. This should rather be an |
76 | // array, as seen in MallocChecker and CStringChecker. |
77 | CheckerNameRef OriginalName; |
78 | |
79 | void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; |
80 | |
81 | /// Fill in the return value that results from messaging nil based on the |
82 | /// return type and architecture and diagnose if the return value will be |
83 | /// garbage. |
84 | void checkObjCMessageNil(const ObjCMethodCall &msg, CheckerContext &C) const; |
85 | |
86 | void checkPreCall(const CallEvent &Call, CheckerContext &C) const; |
87 | |
88 | ProgramStateRef checkFunctionPointerCall(const CallExpr *CE, |
89 | CheckerContext &C, |
90 | ProgramStateRef State) const; |
91 | |
92 | ProgramStateRef checkCXXMethodCall(const CXXInstanceCall *CC, |
93 | CheckerContext &C, |
94 | ProgramStateRef State) const; |
95 | |
96 | ProgramStateRef checkParameterCount(const CallEvent &Call, CheckerContext &C, |
97 | ProgramStateRef State) const; |
98 | |
99 | ProgramStateRef checkCXXDeallocation(const CXXDeallocatorCall *DC, |
100 | CheckerContext &C, |
101 | ProgramStateRef State) const; |
102 | |
103 | ProgramStateRef checkArgInitializedness(const CallEvent &Call, |
104 | CheckerContext &C, |
105 | ProgramStateRef State) const; |
106 | |
107 | private: |
108 | bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange ArgRange, |
109 | const Expr *ArgEx, int ArgumentNumber, |
110 | bool CheckUninitFields, const CallEvent &Call, |
111 | std::unique_ptr<BugType> &BT, |
112 | const ParmVarDecl *ParamDecl) const; |
113 | |
114 | static void emitBadCall(BugType *BT, CheckerContext &C, const Expr *BadE); |
115 | void emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg, |
116 | ExplodedNode *N) const; |
117 | |
118 | void HandleNilReceiver(CheckerContext &C, |
119 | ProgramStateRef state, |
120 | const ObjCMethodCall &msg) const; |
121 | |
122 | void LazyInit_BT(const char *desc, std::unique_ptr<BugType> &BT) const { |
123 | if (!BT) |
124 | BT.reset(p: new BugType(OriginalName, desc)); |
125 | } |
126 | bool uninitRefOrPointer(CheckerContext &C, SVal V, SourceRange ArgRange, |
127 | const Expr *ArgEx, std::unique_ptr<BugType> &BT, |
128 | const ParmVarDecl *ParamDecl, const char *BD, |
129 | int ArgumentNumber) const; |
130 | }; |
131 | } // end anonymous namespace |
132 | |
133 | void CallAndMessageChecker::emitBadCall(BugType *BT, CheckerContext &C, |
134 | const Expr *BadE) { |
135 | ExplodedNode *N = C.generateErrorNode(); |
136 | if (!N) |
137 | return; |
138 | |
139 | auto R = std::make_unique<PathSensitiveBugReport>(args&: *BT, args: BT->getDescription(), args&: N); |
140 | if (BadE) { |
141 | R->addRange(R: BadE->getSourceRange()); |
142 | if (BadE->isGLValue()) |
143 | BadE = bugreporter::getDerefExpr(S: BadE); |
144 | bugreporter::trackExpressionValue(N, E: BadE, R&: *R); |
145 | } |
146 | C.emitReport(R: std::move(R)); |
147 | } |
148 | |
149 | static void describeUninitializedArgumentInCall(const CallEvent &Call, |
150 | int ArgumentNumber, |
151 | llvm::raw_svector_ostream &Os) { |
152 | switch (Call.getKind()) { |
153 | case CE_ObjCMessage: { |
154 | const ObjCMethodCall &Msg = cast<ObjCMethodCall>(Val: Call); |
155 | switch (Msg.getMessageKind()) { |
156 | case OCM_Message: |
157 | Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(Val: ArgumentNumber + 1) |
158 | << " argument in message expression is an uninitialized value" ; |
159 | return; |
160 | case OCM_PropertyAccess: |
161 | assert(Msg.isSetter() && "Getters have no args" ); |
162 | Os << "Argument for property setter is an uninitialized value" ; |
163 | return; |
164 | case OCM_Subscript: |
165 | if (Msg.isSetter() && (ArgumentNumber == 0)) |
166 | Os << "Argument for subscript setter is an uninitialized value" ; |
167 | else |
168 | Os << "Subscript index is an uninitialized value" ; |
169 | return; |
170 | } |
171 | llvm_unreachable("Unknown message kind." ); |
172 | } |
173 | case CE_Block: |
174 | Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(Val: ArgumentNumber + 1) |
175 | << " block call argument is an uninitialized value" ; |
176 | return; |
177 | default: |
178 | Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(Val: ArgumentNumber + 1) |
179 | << " function call argument is an uninitialized value" ; |
180 | return; |
181 | } |
182 | } |
183 | |
184 | bool CallAndMessageChecker::uninitRefOrPointer( |
185 | CheckerContext &C, SVal V, SourceRange ArgRange, const Expr *ArgEx, |
186 | std::unique_ptr<BugType> &BT, const ParmVarDecl *ParamDecl, const char *BD, |
187 | int ArgumentNumber) const { |
188 | |
189 | // The pointee being uninitialized is a sign of code smell, not a bug, no need |
190 | // to sink here. |
191 | if (!ChecksEnabled[CK_ArgPointeeInitializedness]) |
192 | return false; |
193 | |
194 | // No parameter declaration available, i.e. variadic function argument. |
195 | if(!ParamDecl) |
196 | return false; |
197 | |
198 | // If parameter is declared as pointer to const in function declaration, |
199 | // then check if corresponding argument in function call is |
200 | // pointing to undefined symbol value (uninitialized memory). |
201 | SmallString<200> Buf; |
202 | llvm::raw_svector_ostream Os(Buf); |
203 | |
204 | if (ParamDecl->getType()->isPointerType()) { |
205 | Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(Val: ArgumentNumber + 1) |
206 | << " function call argument is a pointer to uninitialized value" ; |
207 | } else if (ParamDecl->getType()->isReferenceType()) { |
208 | Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(Val: ArgumentNumber + 1) |
209 | << " function call argument is an uninitialized value" ; |
210 | } else |
211 | return false; |
212 | |
213 | if(!ParamDecl->getType()->getPointeeType().isConstQualified()) |
214 | return false; |
215 | |
216 | if (const MemRegion *SValMemRegion = V.getAsRegion()) { |
217 | const ProgramStateRef State = C.getState(); |
218 | const SVal PSV = State->getSVal(R: SValMemRegion, T: C.getASTContext().CharTy); |
219 | if (PSV.isUndef()) { |
220 | if (ExplodedNode *N = C.generateErrorNode()) { |
221 | LazyInit_BT(desc: BD, BT); |
222 | auto R = std::make_unique<PathSensitiveBugReport>(args&: *BT, args: Os.str(), args&: N); |
223 | R->addRange(R: ArgRange); |
224 | if (ArgEx) |
225 | bugreporter::trackExpressionValue(N, E: ArgEx, R&: *R); |
226 | |
227 | C.emitReport(R: std::move(R)); |
228 | } |
229 | return true; |
230 | } |
231 | } |
232 | return false; |
233 | } |
234 | |
235 | namespace { |
236 | class FindUninitializedField { |
237 | public: |
238 | SmallVector<const FieldDecl *, 10> FieldChain; |
239 | |
240 | private: |
241 | StoreManager &StoreMgr; |
242 | MemRegionManager &MrMgr; |
243 | Store store; |
244 | |
245 | public: |
246 | FindUninitializedField(StoreManager &storeMgr, MemRegionManager &mrMgr, |
247 | Store s) |
248 | : StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {} |
249 | |
250 | bool Find(const TypedValueRegion *R) { |
251 | QualType T = R->getValueType(); |
252 | if (const RecordType *RT = T->getAsStructureType()) { |
253 | const RecordDecl *RD = RT->getDecl()->getDefinition(); |
254 | assert(RD && "Referred record has no definition" ); |
255 | for (const auto *I : RD->fields()) { |
256 | if (I->isUnnamedBitField()) |
257 | continue; |
258 | const FieldRegion *FR = MrMgr.getFieldRegion(fd: I, superRegion: R); |
259 | FieldChain.push_back(Elt: I); |
260 | T = I->getType(); |
261 | if (T->getAsStructureType()) { |
262 | if (Find(R: FR)) |
263 | return true; |
264 | } else { |
265 | SVal V = StoreMgr.getBinding(store, loc: 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(Ex: 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(S: 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, E: DE, R&: *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 | |
677 | // Check the return type of the message expression. A message to nil will |
678 | // return different values depending on the return type and the architecture. |
679 | QualType RetTy = Msg.getResultType(); |
680 | CanQualType CanRetTy = Ctx.getCanonicalType(T: RetTy); |
681 | const LocationContext *LCtx = C.getLocationContext(); |
682 | |
683 | if (CanRetTy->isStructureOrClassType()) { |
684 | // Structure returns are safe since the compiler zeroes them out. |
685 | SVal V = C.getSValBuilder().makeZeroVal(type: RetTy); |
686 | C.addTransition(State: state->BindExpr(S: Msg.getOriginExpr(), LCtx, V)); |
687 | return; |
688 | } |
689 | |
690 | // Other cases: check if sizeof(return type) > sizeof(void*) |
691 | if (CanRetTy != Ctx.VoidTy && C.getLocationContext()->getParentMap() |
692 | .isConsumedExpr(E: Msg.getOriginExpr())) { |
693 | // Compute: sizeof(void *) and sizeof(return type) |
694 | const uint64_t voidPtrSize = Ctx.getTypeSize(T: Ctx.VoidPtrTy); |
695 | const uint64_t returnTypeSize = Ctx.getTypeSize(T: CanRetTy); |
696 | |
697 | if (CanRetTy.getTypePtr()->isReferenceType()|| |
698 | (voidPtrSize < returnTypeSize && |
699 | !(supportsNilWithFloatRet(triple: Ctx.getTargetInfo().getTriple()) && |
700 | (Ctx.FloatTy == CanRetTy || |
701 | Ctx.DoubleTy == CanRetTy || |
702 | Ctx.LongDoubleTy == CanRetTy || |
703 | Ctx.LongLongTy == CanRetTy || |
704 | Ctx.UnsignedLongLongTy == CanRetTy)))) { |
705 | if (ExplodedNode *N = C.generateErrorNode(State: state)) |
706 | emitNilReceiverBug(C, msg: Msg, N); |
707 | return; |
708 | } |
709 | |
710 | // Handle the safe cases where the return value is 0 if the |
711 | // receiver is nil. |
712 | // |
713 | // FIXME: For now take the conservative approach that we only |
714 | // return null values if we *know* that the receiver is nil. |
715 | // This is because we can have surprises like: |
716 | // |
717 | // ... = [[NSScreens screens] objectAtIndex:0]; |
718 | // |
719 | // What can happen is that [... screens] could return nil, but |
720 | // it most likely isn't nil. We should assume the semantics |
721 | // of this case unless we have *a lot* more knowledge. |
722 | // |
723 | SVal V = C.getSValBuilder().makeZeroVal(type: RetTy); |
724 | C.addTransition(State: state->BindExpr(S: Msg.getOriginExpr(), LCtx, V)); |
725 | return; |
726 | } |
727 | |
728 | C.addTransition(State: state); |
729 | } |
730 | |
731 | void ento::registerCallAndMessageModeling(CheckerManager &mgr) { |
732 | mgr.registerChecker<CallAndMessageChecker>(); |
733 | } |
734 | |
735 | bool ento::shouldRegisterCallAndMessageModeling(const CheckerManager &mgr) { |
736 | return true; |
737 | } |
738 | |
739 | void ento::registerCallAndMessageChecker(CheckerManager &mgr) { |
740 | CallAndMessageChecker *checker = mgr.getChecker<CallAndMessageChecker>(); |
741 | |
742 | checker->OriginalName = mgr.getCurrentCheckerName(); |
743 | |
744 | #define QUERY_CHECKER_OPTION(OPTION) \ |
745 | checker->ChecksEnabled[CallAndMessageChecker::CK_##OPTION] = \ |
746 | mgr.getAnalyzerOptions().getCheckerBooleanOption( \ |
747 | mgr.getCurrentCheckerName(), #OPTION); |
748 | |
749 | QUERY_CHECKER_OPTION(FunctionPointer) |
750 | QUERY_CHECKER_OPTION(ParameterCount) |
751 | QUERY_CHECKER_OPTION(CXXThisMethodCall) |
752 | QUERY_CHECKER_OPTION(CXXDeallocationArg) |
753 | QUERY_CHECKER_OPTION(ArgInitializedness) |
754 | QUERY_CHECKER_OPTION(ArgPointeeInitializedness) |
755 | QUERY_CHECKER_OPTION(NilReceiver) |
756 | QUERY_CHECKER_OPTION(UndefReceiver) |
757 | } |
758 | |
759 | bool ento::shouldRegisterCallAndMessageChecker(const CheckerManager &mgr) { |
760 | return true; |
761 | } |
762 | |