1//== RetainSummaryManager.cpp - Summaries for reference counting --*- 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 file defines summaries implementation for retain counting, which
10// implements a reference count checker for Core Foundation, Cocoa
11// and OSObject (on Mac OS X).
12//
13//===----------------------------------------------------------------------===//
14
15#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
16#include "clang/Analysis/RetainSummaryManager.h"
17#include "clang/AST/Attr.h"
18#include "clang/AST/DeclCXX.h"
19#include "clang/AST/DeclObjC.h"
20#include "clang/AST/ParentMap.h"
21#include "clang/ASTMatchers/ASTMatchFinder.h"
22#include <optional>
23
24using namespace clang;
25using namespace ento;
26
27template <class T>
28constexpr static bool isOneOf() {
29 return false;
30}
31
32/// Helper function to check whether the class is one of the
33/// rest of varargs.
34template <class T, class P, class... ToCompare>
35constexpr static bool isOneOf() {
36 return std::is_same_v<T, P> || isOneOf<T, ToCompare...>();
37}
38
39namespace {
40
41/// Fake attribute class for RC* attributes.
42struct GeneralizedReturnsRetainedAttr {
43 static bool classof(const Attr *A) {
44 if (auto AA = dyn_cast<AnnotateAttr>(Val: A))
45 return AA->getAnnotation() == "rc_ownership_returns_retained";
46 return false;
47 }
48};
49
50struct GeneralizedReturnsNotRetainedAttr {
51 static bool classof(const Attr *A) {
52 if (auto AA = dyn_cast<AnnotateAttr>(Val: A))
53 return AA->getAnnotation() == "rc_ownership_returns_not_retained";
54 return false;
55 }
56};
57
58struct GeneralizedConsumedAttr {
59 static bool classof(const Attr *A) {
60 if (auto AA = dyn_cast<AnnotateAttr>(Val: A))
61 return AA->getAnnotation() == "rc_ownership_consumed";
62 return false;
63 }
64};
65
66}
67
68template <class T>
69std::optional<ObjKind> RetainSummaryManager::hasAnyEnabledAttrOf(const Decl *D,
70 QualType QT) {
71 ObjKind K;
72 if (isOneOf<T, CFConsumedAttr, CFReturnsRetainedAttr,
73 CFReturnsNotRetainedAttr>()) {
74 if (!TrackObjCAndCFObjects)
75 return std::nullopt;
76
77 K = ObjKind::CF;
78 } else if (isOneOf<T, NSConsumedAttr, NSConsumesSelfAttr,
79 NSReturnsAutoreleasedAttr, NSReturnsRetainedAttr,
80 NSReturnsNotRetainedAttr, NSConsumesSelfAttr>()) {
81
82 if (!TrackObjCAndCFObjects)
83 return std::nullopt;
84
85 if (isOneOf<T, NSReturnsRetainedAttr, NSReturnsAutoreleasedAttr,
86 NSReturnsNotRetainedAttr>() &&
87 !cocoa::isCocoaObjectRef(T: QT))
88 return std::nullopt;
89 K = ObjKind::ObjC;
90 } else if (isOneOf<T, OSConsumedAttr, OSConsumesThisAttr,
91 OSReturnsNotRetainedAttr, OSReturnsRetainedAttr,
92 OSReturnsRetainedOnZeroAttr,
93 OSReturnsRetainedOnNonZeroAttr>()) {
94 if (!TrackOSObjects)
95 return std::nullopt;
96 K = ObjKind::OS;
97 } else if (isOneOf<T, GeneralizedReturnsNotRetainedAttr,
98 GeneralizedReturnsRetainedAttr,
99 GeneralizedConsumedAttr>()) {
100 K = ObjKind::Generalized;
101 } else {
102 llvm_unreachable("Unexpected attribute");
103 }
104 if (D->hasAttr<T>())
105 return K;
106 return std::nullopt;
107}
108
109template <class T1, class T2, class... Others>
110std::optional<ObjKind> RetainSummaryManager::hasAnyEnabledAttrOf(const Decl *D,
111 QualType QT) {
112 if (auto Out = hasAnyEnabledAttrOf<T1>(D, QT))
113 return Out;
114 return hasAnyEnabledAttrOf<T2, Others...>(D, QT);
115}
116
117const RetainSummary *
118RetainSummaryManager::getPersistentSummary(const RetainSummary &OldSumm) {
119 // Unique "simple" summaries -- those without ArgEffects.
120 if (OldSumm.isSimple()) {
121 ::llvm::FoldingSetNodeID ID;
122 OldSumm.Profile(ID);
123
124 void *Pos;
125 CachedSummaryNode *N = SimpleSummaries.FindNodeOrInsertPos(ID, InsertPos&: Pos);
126
127 if (!N) {
128 N = (CachedSummaryNode *) BPAlloc.Allocate<CachedSummaryNode>();
129 new (N) CachedSummaryNode(OldSumm);
130 SimpleSummaries.InsertNode(N, InsertPos: Pos);
131 }
132
133 return &N->getValue();
134 }
135
136 RetainSummary *Summ = (RetainSummary *) BPAlloc.Allocate<RetainSummary>();
137 new (Summ) RetainSummary(OldSumm);
138 return Summ;
139}
140
141static bool isSubclass(const Decl *D,
142 StringRef ClassName) {
143 using namespace ast_matchers;
144 DeclarationMatcher SubclassM =
145 cxxRecordDecl(isSameOrDerivedFrom(BaseName: std::string(ClassName)));
146 return !(match(Matcher: SubclassM, Node: *D, Context&: D->getASTContext()).empty());
147}
148
149static bool isExactClass(const Decl *D, StringRef ClassName) {
150 using namespace ast_matchers;
151 DeclarationMatcher sameClassM =
152 cxxRecordDecl(hasName(Name: std::string(ClassName)));
153 return !(match(Matcher: sameClassM, Node: *D, Context&: D->getASTContext()).empty());
154}
155
156static bool isOSObjectSubclass(const Decl *D) {
157 return D && isSubclass(D, ClassName: "OSMetaClassBase") &&
158 !isExactClass(D, ClassName: "OSMetaClass");
159}
160
161static bool isOSObjectDynamicCast(StringRef S) { return S == "safeMetaCast"; }
162
163static bool isOSObjectRequiredCast(StringRef S) {
164 return S == "requiredMetaCast";
165}
166
167static bool isOSObjectThisCast(StringRef S) {
168 return S == "metaCast";
169}
170
171
172static bool isOSObjectPtr(QualType QT) {
173 return isOSObjectSubclass(D: QT->getPointeeCXXRecordDecl());
174}
175
176static bool isISLObjectRef(QualType Ty) {
177 return StringRef(Ty.getAsString()).starts_with(Prefix: "isl_");
178}
179
180static bool isOSIteratorSubclass(const Decl *D) {
181 return isSubclass(D, ClassName: "OSIterator");
182}
183
184static bool hasRCAnnotation(const Decl *D, StringRef rcAnnotation) {
185 for (const auto *Ann : D->specific_attrs<AnnotateAttr>()) {
186 if (Ann->getAnnotation() == rcAnnotation)
187 return true;
188 }
189 return false;
190}
191
192static bool isRetain(const FunctionDecl *FD, StringRef FName) {
193 return FName.starts_with_insensitive(Prefix: "retain") ||
194 FName.ends_with_insensitive(Suffix: "retain");
195}
196
197static bool isRelease(const FunctionDecl *FD, StringRef FName) {
198 return FName.starts_with_insensitive(Prefix: "release") ||
199 FName.ends_with_insensitive(Suffix: "release");
200}
201
202static bool isAutorelease(const FunctionDecl *FD, StringRef FName) {
203 return FName.starts_with_insensitive(Prefix: "autorelease") ||
204 FName.ends_with_insensitive(Suffix: "autorelease");
205}
206
207static bool isMakeCollectable(StringRef FName) {
208 return FName.contains_insensitive(Other: "MakeCollectable");
209}
210
211/// A function is OSObject related if it is declared on a subclass
212/// of OSObject, or any of the parameters is a subclass of an OSObject.
213static bool isOSObjectRelated(const CXXMethodDecl *MD) {
214 if (isOSObjectSubclass(D: MD->getParent()))
215 return true;
216
217 for (ParmVarDecl *Param : MD->parameters()) {
218 QualType PT = Param->getType()->getPointeeType();
219 if (!PT.isNull())
220 if (CXXRecordDecl *RD = PT->getAsCXXRecordDecl())
221 if (isOSObjectSubclass(D: RD))
222 return true;
223 }
224
225 return false;
226}
227
228bool
229RetainSummaryManager::isKnownSmartPointer(QualType QT) {
230 QT = QT.getCanonicalType();
231 const auto *RD = QT->getAsCXXRecordDecl();
232 if (!RD)
233 return false;
234 const IdentifierInfo *II = RD->getIdentifier();
235 if (II && II->getName() == "smart_ptr")
236 if (const auto *ND = dyn_cast<NamespaceDecl>(Val: RD->getDeclContext()))
237 if (ND->getNameAsString() == "os")
238 return true;
239 return false;
240}
241
242const RetainSummary *
243RetainSummaryManager::getSummaryForOSObject(const FunctionDecl *FD,
244 StringRef FName, QualType RetTy) {
245 assert(TrackOSObjects &&
246 "Requesting a summary for an OSObject but OSObjects are not tracked");
247
248 if (RetTy->isPointerType()) {
249 const CXXRecordDecl *PD = RetTy->getPointeeType()->getAsCXXRecordDecl();
250 if (PD && isOSObjectSubclass(D: PD)) {
251 if (isOSObjectDynamicCast(S: FName) || isOSObjectRequiredCast(S: FName) ||
252 isOSObjectThisCast(S: FName))
253 return getDefaultSummary();
254
255 // TODO: Add support for the slightly common *Matching(table) idiom.
256 // Cf. IOService::nameMatching() etc. - these function have an unusual
257 // contract of returning at +0 or +1 depending on their last argument.
258 if (FName.ends_with(Suffix: "Matching")) {
259 return getPersistentStopSummary();
260 }
261
262 // All objects returned with functions *not* starting with 'get',
263 // or iterators, are returned at +1.
264 if ((!FName.starts_with(Prefix: "get") && !FName.starts_with(Prefix: "Get")) ||
265 isOSIteratorSubclass(D: PD)) {
266 return getOSSummaryCreateRule(FD);
267 } else {
268 return getOSSummaryGetRule(FD);
269 }
270 }
271 }
272
273 if (const auto *MD = dyn_cast<CXXMethodDecl>(Val: FD)) {
274 const CXXRecordDecl *Parent = MD->getParent();
275 if (Parent && isOSObjectSubclass(D: Parent)) {
276 if (FName == "release" || FName == "taggedRelease")
277 return getOSSummaryReleaseRule(FD);
278
279 if (FName == "retain" || FName == "taggedRetain")
280 return getOSSummaryRetainRule(FD);
281
282 if (FName == "free")
283 return getOSSummaryFreeRule(FD);
284
285 if (MD->getOverloadedOperator() == OO_New)
286 return getOSSummaryCreateRule(FD: MD);
287 }
288 }
289
290 return nullptr;
291}
292
293const RetainSummary *RetainSummaryManager::getSummaryForObjCOrCFObject(
294 const FunctionDecl *FD,
295 StringRef FName,
296 QualType RetTy,
297 const FunctionType *FT,
298 bool &AllowAnnotations) {
299
300 ArgEffects ScratchArgs(AF.getEmptyMap());
301
302 std::string RetTyName = RetTy.getAsString();
303 if (FName == "pthread_create" || FName == "pthread_setspecific") {
304 // It's not uncommon to pass a tracked object into the thread
305 // as 'void *arg', and then release it inside the thread.
306 // FIXME: We could build a much more precise model for these functions.
307 return getPersistentStopSummary();
308 } else if(FName == "NSMakeCollectable") {
309 // Handle: id NSMakeCollectable(CFTypeRef)
310 AllowAnnotations = false;
311 return RetTy->isObjCIdType() ? getUnarySummary(FT, AE: DoNothing)
312 : getPersistentStopSummary();
313 } else if (FName == "CMBufferQueueDequeueAndRetain" ||
314 FName == "CMBufferQueueDequeueIfDataReadyAndRetain") {
315 // These API functions are known to NOT act as a CFRetain wrapper.
316 // They simply make a new object owned by the caller.
317 return getPersistentSummary(RetEff: RetEffect::MakeOwned(o: ObjKind::CF),
318 ScratchArgs,
319 ReceiverEff: ArgEffect(DoNothing),
320 DefaultEff: ArgEffect(DoNothing));
321 } else if (FName == "CFPlugInInstanceCreate") {
322 return getPersistentSummary(RetEff: RetEffect::MakeNoRet(), ScratchArgs);
323 } else if (FName == "IORegistryEntrySearchCFProperty" ||
324 (RetTyName == "CFMutableDictionaryRef" &&
325 (FName == "IOBSDNameMatching" || FName == "IOServiceMatching" ||
326 FName == "IOServiceNameMatching" ||
327 FName == "IORegistryEntryIDMatching" ||
328 FName == "IOOpenFirmwarePathMatching"))) {
329 // Yes, these IOKit functions return CF objects.
330 // They also violate the CF naming convention.
331 return getPersistentSummary(RetEff: RetEffect::MakeOwned(o: ObjKind::CF), ScratchArgs,
332 ReceiverEff: ArgEffect(DoNothing), DefaultEff: ArgEffect(DoNothing));
333 } else if (FName == "IOServiceGetMatchingService" ||
334 FName == "IOServiceGetMatchingServices") {
335 // These IOKit functions accept CF objects as arguments.
336 // They also consume them without an appropriate annotation.
337 ScratchArgs = AF.add(Old: ScratchArgs, K: 1, D: ArgEffect(DecRef, ObjKind::CF));
338 return getPersistentSummary(RetEff: RetEffect::MakeNoRet(),
339 ScratchArgs,
340 ReceiverEff: ArgEffect(DoNothing), DefaultEff: ArgEffect(DoNothing));
341 } else if (FName == "IOServiceAddNotification" ||
342 FName == "IOServiceAddMatchingNotification") {
343 // More IOKit functions suddenly accepting (and even more suddenly,
344 // consuming) CF objects.
345 ScratchArgs = AF.add(Old: ScratchArgs, K: 2, D: ArgEffect(DecRef, ObjKind::CF));
346 return getPersistentSummary(RetEff: RetEffect::MakeNoRet(),
347 ScratchArgs,
348 ReceiverEff: ArgEffect(DoNothing), DefaultEff: ArgEffect(DoNothing));
349 } else if (FName == "CVPixelBufferCreateWithBytes") {
350 // Eventually this can be improved by recognizing that the pixel
351 // buffer passed to CVPixelBufferCreateWithBytes is released via
352 // a callback and doing full IPA to make sure this is done correctly.
353 // Note that it's passed as a 'void *', so it's hard to annotate.
354 // FIXME: This function also has an out parameter that returns an
355 // allocated object.
356 ScratchArgs = AF.add(Old: ScratchArgs, K: 7, D: ArgEffect(StopTracking));
357 return getPersistentSummary(RetEff: RetEffect::MakeNoRet(),
358 ScratchArgs,
359 ReceiverEff: ArgEffect(DoNothing), DefaultEff: ArgEffect(DoNothing));
360 } else if (FName == "CGBitmapContextCreateWithData") {
361 // This is similar to the CVPixelBufferCreateWithBytes situation above.
362 // Eventually this can be improved by recognizing that 'releaseInfo'
363 // passed to CGBitmapContextCreateWithData is released via
364 // a callback and doing full IPA to make sure this is done correctly.
365 ScratchArgs = AF.add(Old: ScratchArgs, K: 8, D: ArgEffect(ArgEffect(StopTracking)));
366 return getPersistentSummary(RetEff: RetEffect::MakeOwned(o: ObjKind::CF), ScratchArgs,
367 ReceiverEff: ArgEffect(DoNothing), DefaultEff: ArgEffect(DoNothing));
368 } else if (FName == "CVPixelBufferCreateWithPlanarBytes") {
369 // Same as CVPixelBufferCreateWithBytes, just more arguments.
370 ScratchArgs = AF.add(Old: ScratchArgs, K: 12, D: ArgEffect(StopTracking));
371 return getPersistentSummary(RetEff: RetEffect::MakeNoRet(),
372 ScratchArgs,
373 ReceiverEff: ArgEffect(DoNothing), DefaultEff: ArgEffect(DoNothing));
374 } else if (FName == "VTCompressionSessionEncodeFrame" ||
375 FName == "VTCompressionSessionEncodeMultiImageFrame") {
376 // The context argument passed to VTCompressionSessionEncodeFrame() et.al.
377 // is passed to the callback specified when creating the session
378 // (e.g. with VTCompressionSessionCreate()) which can release it.
379 // To account for this possibility, conservatively stop tracking
380 // the context.
381 ScratchArgs = AF.add(Old: ScratchArgs, K: 5, D: ArgEffect(StopTracking));
382 return getPersistentSummary(RetEff: RetEffect::MakeNoRet(),
383 ScratchArgs,
384 ReceiverEff: ArgEffect(DoNothing), DefaultEff: ArgEffect(DoNothing));
385 } else if (FName == "dispatch_set_context" ||
386 FName == "xpc_connection_set_context") {
387 // The analyzer currently doesn't have a good way to reason about
388 // dispatch_set_finalizer_f() which typically cleans up the context.
389 // If we pass a context object that is memory managed, stop tracking it.
390 // Same with xpc_connection_set_finalizer_f().
391 ScratchArgs = AF.add(Old: ScratchArgs, K: 1, D: ArgEffect(StopTracking));
392 return getPersistentSummary(RetEff: RetEffect::MakeNoRet(),
393 ScratchArgs,
394 ReceiverEff: ArgEffect(DoNothing), DefaultEff: ArgEffect(DoNothing));
395 } else if (FName.starts_with(Prefix: "NSLog")) {
396 return getDoNothingSummary();
397 } else if (FName.starts_with(Prefix: "NS") && FName.contains(Other: "Insert")) {
398 // Allowlist NSXXInsertXX, for example NSMapInsertIfAbsent, since they can
399 // be deallocated by NSMapRemove.
400 ScratchArgs = AF.add(Old: ScratchArgs, K: 1, D: ArgEffect(StopTracking));
401 ScratchArgs = AF.add(Old: ScratchArgs, K: 2, D: ArgEffect(StopTracking));
402 return getPersistentSummary(RetEff: RetEffect::MakeNoRet(),
403 ScratchArgs, ReceiverEff: ArgEffect(DoNothing),
404 DefaultEff: ArgEffect(DoNothing));
405 }
406
407 if (RetTy->isPointerType()) {
408
409 // For CoreFoundation ('CF') types.
410 if (cocoa::isRefType(RetTy, Prefix: "CF", Name: FName)) {
411 if (isRetain(FD, FName)) {
412 // CFRetain isn't supposed to be annotated. However, this may as
413 // well be a user-made "safe" CFRetain function that is incorrectly
414 // annotated as cf_returns_retained due to lack of better options.
415 // We want to ignore such annotation.
416 AllowAnnotations = false;
417
418 return getUnarySummary(FT, AE: IncRef);
419 } else if (isAutorelease(FD, FName)) {
420 // The headers use cf_consumed, but we can fully model CFAutorelease
421 // ourselves.
422 AllowAnnotations = false;
423
424 return getUnarySummary(FT, AE: Autorelease);
425 } else if (isMakeCollectable(FName)) {
426 AllowAnnotations = false;
427 return getUnarySummary(FT, AE: DoNothing);
428 } else {
429 return getCFCreateGetRuleSummary(FD);
430 }
431 }
432
433 // For CoreGraphics ('CG') and CoreVideo ('CV') types.
434 if (cocoa::isRefType(RetTy, Prefix: "CG", Name: FName) ||
435 cocoa::isRefType(RetTy, Prefix: "CV", Name: FName)) {
436 if (isRetain(FD, FName))
437 return getUnarySummary(FT, AE: IncRef);
438 else
439 return getCFCreateGetRuleSummary(FD);
440 }
441
442 // For all other CF-style types, use the Create/Get
443 // rule for summaries but don't support Retain functions
444 // with framework-specific prefixes.
445 if (coreFoundation::isCFObjectRef(T: RetTy)) {
446 return getCFCreateGetRuleSummary(FD);
447 }
448
449 if (FD->hasAttr<CFAuditedTransferAttr>()) {
450 return getCFCreateGetRuleSummary(FD);
451 }
452 }
453
454 // Check for release functions, the only kind of functions that we care
455 // about that don't return a pointer type.
456 if (FName.starts_with(Prefix: "CG") || FName.starts_with(Prefix: "CF")) {
457 // Test for 'CGCF'.
458 FName = FName.substr(Start: FName.starts_with(Prefix: "CGCF") ? 4 : 2);
459
460 if (isRelease(FD, FName))
461 return getUnarySummary(FT, AE: DecRef);
462 else {
463 assert(ScratchArgs.isEmpty());
464 // Remaining CoreFoundation and CoreGraphics functions.
465 // We use to assume that they all strictly followed the ownership idiom
466 // and that ownership cannot be transferred. While this is technically
467 // correct, many methods allow a tracked object to escape. For example:
468 //
469 // CFMutableDictionaryRef x = CFDictionaryCreateMutable(...);
470 // CFDictionaryAddValue(y, key, x);
471 // CFRelease(x);
472 // ... it is okay to use 'x' since 'y' has a reference to it
473 //
474 // We handle this and similar cases with the follow heuristic. If the
475 // function name contains "InsertValue", "SetValue", "AddValue",
476 // "AppendValue", or "SetAttribute", then we assume that arguments may
477 // "escape." This means that something else holds on to the object,
478 // allowing it be used even after its local retain count drops to 0.
479 ArgEffectKind E =
480 (StrInStrNoCase(s1: FName, s2: "InsertValue") != StringRef::npos ||
481 StrInStrNoCase(s1: FName, s2: "AddValue") != StringRef::npos ||
482 StrInStrNoCase(s1: FName, s2: "SetValue") != StringRef::npos ||
483 StrInStrNoCase(s1: FName, s2: "AppendValue") != StringRef::npos ||
484 StrInStrNoCase(s1: FName, s2: "SetAttribute") != StringRef::npos)
485 ? MayEscape
486 : DoNothing;
487
488 return getPersistentSummary(RetEff: RetEffect::MakeNoRet(), ScratchArgs,
489 ReceiverEff: ArgEffect(DoNothing), DefaultEff: ArgEffect(E, ObjKind::CF));
490 }
491 }
492
493 return nullptr;
494}
495
496const RetainSummary *
497RetainSummaryManager::generateSummary(const FunctionDecl *FD,
498 bool &AllowAnnotations) {
499 // We generate "stop" summaries for implicitly defined functions.
500 if (FD->isImplicit())
501 return getPersistentStopSummary();
502
503 const IdentifierInfo *II = FD->getIdentifier();
504
505 StringRef FName = II ? II->getName() : "";
506
507 // Strip away preceding '_'. Doing this here will effect all the checks
508 // down below.
509 FName = FName.substr(Start: FName.find_first_not_of(C: '_'));
510
511 // Inspect the result type. Strip away any typedefs.
512 const auto *FT = FD->getType()->castAs<FunctionType>();
513 QualType RetTy = FT->getReturnType();
514
515 if (TrackOSObjects)
516 if (const RetainSummary *S = getSummaryForOSObject(FD, FName, RetTy))
517 return S;
518
519 if (const auto *MD = dyn_cast<CXXMethodDecl>(Val: FD))
520 if (!isOSObjectRelated(MD))
521 return getPersistentSummary(RetEff: RetEffect::MakeNoRet(),
522 ScratchArgs: ArgEffects(AF.getEmptyMap()),
523 ReceiverEff: ArgEffect(DoNothing),
524 DefaultEff: ArgEffect(StopTracking),
525 ThisEff: ArgEffect(DoNothing));
526
527 if (TrackObjCAndCFObjects)
528 if (const RetainSummary *S =
529 getSummaryForObjCOrCFObject(FD, FName, RetTy, FT, AllowAnnotations))
530 return S;
531
532 return getDefaultSummary();
533}
534
535const RetainSummary *
536RetainSummaryManager::getFunctionSummary(const FunctionDecl *FD) {
537 // If we don't know what function we're calling, use our default summary.
538 if (!FD)
539 return getDefaultSummary();
540
541 // Look up a summary in our cache of FunctionDecls -> Summaries.
542 FuncSummariesTy::iterator I = FuncSummaries.find(Val: FD);
543 if (I != FuncSummaries.end())
544 return I->second;
545
546 // No summary? Generate one.
547 bool AllowAnnotations = true;
548 const RetainSummary *S = generateSummary(FD, AllowAnnotations);
549
550 // Annotations override defaults.
551 if (AllowAnnotations)
552 updateSummaryFromAnnotations(Summ&: S, FD);
553
554 FuncSummaries[FD] = S;
555 return S;
556}
557
558//===----------------------------------------------------------------------===//
559// Summary creation for functions (largely uses of Core Foundation).
560//===----------------------------------------------------------------------===//
561
562static ArgEffect getStopTrackingHardEquivalent(ArgEffect E) {
563 switch (E.getKind()) {
564 case DoNothing:
565 case Autorelease:
566 case DecRefBridgedTransferred:
567 case IncRef:
568 case UnretainedOutParameter:
569 case RetainedOutParameter:
570 case RetainedOutParameterOnZero:
571 case RetainedOutParameterOnNonZero:
572 case MayEscape:
573 case StopTracking:
574 case StopTrackingHard:
575 return E.withKind(NewK: StopTrackingHard);
576 case DecRef:
577 case DecRefAndStopTrackingHard:
578 return E.withKind(NewK: DecRefAndStopTrackingHard);
579 case Dealloc:
580 return E.withKind(NewK: Dealloc);
581 }
582
583 llvm_unreachable("Unknown ArgEffect kind");
584}
585
586const RetainSummary *
587RetainSummaryManager::updateSummaryForNonZeroCallbackArg(const RetainSummary *S,
588 AnyCall &C) {
589 ArgEffect RecEffect = getStopTrackingHardEquivalent(E: S->getReceiverEffect());
590 ArgEffect DefEffect = getStopTrackingHardEquivalent(E: S->getDefaultArgEffect());
591
592 ArgEffects ScratchArgs(AF.getEmptyMap());
593 ArgEffects CustomArgEffects = S->getArgEffects();
594 for (ArgEffects::iterator I = CustomArgEffects.begin(),
595 E = CustomArgEffects.end();
596 I != E; ++I) {
597 ArgEffect Translated = getStopTrackingHardEquivalent(E: I->second);
598 if (Translated.getKind() != DefEffect.getKind())
599 ScratchArgs = AF.add(Old: ScratchArgs, K: I->first, D: Translated);
600 }
601
602 RetEffect RE = RetEffect::MakeNoRetHard();
603
604 // Special cases where the callback argument CANNOT free the return value.
605 // This can generally only happen if we know that the callback will only be
606 // called when the return value is already being deallocated.
607 if (const IdentifierInfo *Name = C.getIdentifier()) {
608 // When the CGBitmapContext is deallocated, the callback here will free
609 // the associated data buffer.
610 // The callback in dispatch_data_create frees the buffer, but not
611 // the data object.
612 if (Name->isStr(Str: "CGBitmapContextCreateWithData") ||
613 Name->isStr(Str: "dispatch_data_create"))
614 RE = S->getRetEffect();
615 }
616
617 return getPersistentSummary(RetEff: RE, ScratchArgs, ReceiverEff: RecEffect, DefaultEff: DefEffect);
618}
619
620void RetainSummaryManager::updateSummaryForReceiverUnconsumedSelf(
621 const RetainSummary *&S) {
622
623 RetainSummaryTemplate Template(S, *this);
624
625 Template->setReceiverEffect(ArgEffect(DoNothing));
626 Template->setRetEffect(RetEffect::MakeNoRet());
627}
628
629
630void RetainSummaryManager::updateSummaryForArgumentTypes(
631 const AnyCall &C, const RetainSummary *&RS) {
632 RetainSummaryTemplate Template(RS, *this);
633
634 unsigned parm_idx = 0;
635 for (auto pi = C.param_begin(), pe = C.param_end(); pi != pe;
636 ++pi, ++parm_idx) {
637 QualType QT = (*pi)->getType();
638
639 // Skip already created values.
640 if (RS->getArgEffects().contains(K: parm_idx))
641 continue;
642
643 ObjKind K = ObjKind::AnyObj;
644
645 if (isISLObjectRef(Ty: QT)) {
646 K = ObjKind::Generalized;
647 } else if (isOSObjectPtr(QT)) {
648 K = ObjKind::OS;
649 } else if (cocoa::isCocoaObjectRef(T: QT)) {
650 K = ObjKind::ObjC;
651 } else if (coreFoundation::isCFObjectRef(T: QT)) {
652 K = ObjKind::CF;
653 }
654
655 if (K != ObjKind::AnyObj)
656 Template->addArg(af&: AF, idx: parm_idx,
657 e: ArgEffect(RS->getDefaultArgEffect().getKind(), K));
658 }
659}
660
661const RetainSummary *
662RetainSummaryManager::getSummary(AnyCall C,
663 bool HasNonZeroCallbackArg,
664 bool IsReceiverUnconsumedSelf,
665 QualType ReceiverType) {
666 const RetainSummary *Summ;
667 switch (C.getKind()) {
668 case AnyCall::Function:
669 case AnyCall::Constructor:
670 case AnyCall::InheritedConstructor:
671 case AnyCall::Allocator:
672 case AnyCall::Deallocator:
673 Summ = getFunctionSummary(FD: cast_or_null<FunctionDecl>(Val: C.getDecl()));
674 break;
675 case AnyCall::Block:
676 case AnyCall::Destructor:
677 // FIXME: These calls are currently unsupported.
678 return getPersistentStopSummary();
679 case AnyCall::ObjCMethod: {
680 const auto *ME = cast_or_null<ObjCMessageExpr>(Val: C.getExpr());
681 if (!ME) {
682 Summ = getMethodSummary(MD: cast<ObjCMethodDecl>(Val: C.getDecl()));
683 } else if (ME->isInstanceMessage()) {
684 Summ = getInstanceMethodSummary(ME, ReceiverType);
685 } else {
686 Summ = getClassMethodSummary(ME);
687 }
688 break;
689 }
690 }
691
692 if (HasNonZeroCallbackArg)
693 Summ = updateSummaryForNonZeroCallbackArg(S: Summ, C);
694
695 if (IsReceiverUnconsumedSelf)
696 updateSummaryForReceiverUnconsumedSelf(S&: Summ);
697
698 updateSummaryForArgumentTypes(C, RS&: Summ);
699
700 assert(Summ && "Unknown call type?");
701 return Summ;
702}
703
704
705const RetainSummary *
706RetainSummaryManager::getCFCreateGetRuleSummary(const FunctionDecl *FD) {
707 if (coreFoundation::followsCreateRule(FD))
708 return getCFSummaryCreateRule(FD);
709
710 return getCFSummaryGetRule(FD);
711}
712
713bool RetainSummaryManager::isTrustedReferenceCountImplementation(
714 const Decl *FD) {
715 return hasRCAnnotation(D: FD, rcAnnotation: "rc_ownership_trusted_implementation");
716}
717
718std::optional<RetainSummaryManager::BehaviorSummary>
719RetainSummaryManager::canEval(const CallExpr *CE, const FunctionDecl *FD,
720 bool &hasTrustedImplementationAnnotation) {
721
722 IdentifierInfo *II = FD->getIdentifier();
723 if (!II)
724 return std::nullopt;
725
726 StringRef FName = II->getName();
727 FName = FName.substr(Start: FName.find_first_not_of(C: '_'));
728
729 QualType ResultTy = CE->getCallReturnType(Ctx);
730 if (ResultTy->isObjCIdType()) {
731 if (II->isStr(Str: "NSMakeCollectable"))
732 return BehaviorSummary::Identity;
733 } else if (ResultTy->isPointerType()) {
734 // Handle: (CF|CG|CV)Retain
735 // CFAutorelease
736 // It's okay to be a little sloppy here.
737 if (FName == "CMBufferQueueDequeueAndRetain" ||
738 FName == "CMBufferQueueDequeueIfDataReadyAndRetain") {
739 // These API functions are known to NOT act as a CFRetain wrapper.
740 // They simply make a new object owned by the caller.
741 return std::nullopt;
742 }
743 if (CE->getNumArgs() == 1 &&
744 (cocoa::isRefType(RetTy: ResultTy, Prefix: "CF", Name: FName) ||
745 cocoa::isRefType(RetTy: ResultTy, Prefix: "CG", Name: FName) ||
746 cocoa::isRefType(RetTy: ResultTy, Prefix: "CV", Name: FName)) &&
747 (isRetain(FD, FName) || isAutorelease(FD, FName) ||
748 isMakeCollectable(FName)))
749 return BehaviorSummary::Identity;
750
751 // safeMetaCast is called by OSDynamicCast.
752 // We assume that OSDynamicCast is either an identity (cast is OK,
753 // the input was non-zero),
754 // or that it returns zero (when the cast failed, or the input
755 // was zero).
756 if (TrackOSObjects) {
757 if (isOSObjectDynamicCast(S: FName) && FD->param_size() >= 1) {
758 return BehaviorSummary::IdentityOrZero;
759 } else if (isOSObjectRequiredCast(S: FName) && FD->param_size() >= 1) {
760 return BehaviorSummary::Identity;
761 } else if (isOSObjectThisCast(S: FName) && isa<CXXMethodDecl>(Val: FD) &&
762 !cast<CXXMethodDecl>(Val: FD)->isStatic()) {
763 return BehaviorSummary::IdentityThis;
764 }
765 }
766
767 const FunctionDecl* FDD = FD->getDefinition();
768 if (FDD && isTrustedReferenceCountImplementation(FD: FDD)) {
769 hasTrustedImplementationAnnotation = true;
770 return BehaviorSummary::Identity;
771 }
772 }
773
774 if (const auto *MD = dyn_cast<CXXMethodDecl>(Val: FD)) {
775 const CXXRecordDecl *Parent = MD->getParent();
776 if (TrackOSObjects && Parent && isOSObjectSubclass(D: Parent))
777 if (FName == "release" || FName == "retain")
778 return BehaviorSummary::NoOp;
779 }
780
781 return std::nullopt;
782}
783
784const RetainSummary *
785RetainSummaryManager::getUnarySummary(const FunctionType* FT,
786 ArgEffectKind AE) {
787
788 // Unary functions have no arg effects by definition.
789 ArgEffects ScratchArgs(AF.getEmptyMap());
790
791 // Verify that this is *really* a unary function. This can
792 // happen if people do weird things.
793 const FunctionProtoType* FTP = dyn_cast<FunctionProtoType>(Val: FT);
794 if (!FTP || FTP->getNumParams() != 1)
795 return getPersistentStopSummary();
796
797 ArgEffect Effect(AE, ObjKind::CF);
798
799 ScratchArgs = AF.add(Old: ScratchArgs, K: 0, D: Effect);
800 return getPersistentSummary(RetEff: RetEffect::MakeNoRet(),
801 ScratchArgs,
802 ReceiverEff: ArgEffect(DoNothing), DefaultEff: ArgEffect(DoNothing));
803}
804
805const RetainSummary *
806RetainSummaryManager::getOSSummaryRetainRule(const FunctionDecl *FD) {
807 return getPersistentSummary(RetEff: RetEffect::MakeNoRet(),
808 ScratchArgs: AF.getEmptyMap(),
809 /*ReceiverEff=*/ArgEffect(DoNothing),
810 /*DefaultEff=*/ArgEffect(DoNothing),
811 /*ThisEff=*/ArgEffect(IncRef, ObjKind::OS));
812}
813
814const RetainSummary *
815RetainSummaryManager::getOSSummaryReleaseRule(const FunctionDecl *FD) {
816 return getPersistentSummary(RetEff: RetEffect::MakeNoRet(),
817 ScratchArgs: AF.getEmptyMap(),
818 /*ReceiverEff=*/ArgEffect(DoNothing),
819 /*DefaultEff=*/ArgEffect(DoNothing),
820 /*ThisEff=*/ArgEffect(DecRef, ObjKind::OS));
821}
822
823const RetainSummary *
824RetainSummaryManager::getOSSummaryFreeRule(const FunctionDecl *FD) {
825 return getPersistentSummary(RetEff: RetEffect::MakeNoRet(),
826 ScratchArgs: AF.getEmptyMap(),
827 /*ReceiverEff=*/ArgEffect(DoNothing),
828 /*DefaultEff=*/ArgEffect(DoNothing),
829 /*ThisEff=*/ArgEffect(Dealloc, ObjKind::OS));
830}
831
832const RetainSummary *
833RetainSummaryManager::getOSSummaryCreateRule(const FunctionDecl *FD) {
834 return getPersistentSummary(RetEff: RetEffect::MakeOwned(o: ObjKind::OS),
835 ScratchArgs: AF.getEmptyMap());
836}
837
838const RetainSummary *
839RetainSummaryManager::getOSSummaryGetRule(const FunctionDecl *FD) {
840 return getPersistentSummary(RetEff: RetEffect::MakeNotOwned(o: ObjKind::OS),
841 ScratchArgs: AF.getEmptyMap());
842}
843
844const RetainSummary *
845RetainSummaryManager::getCFSummaryCreateRule(const FunctionDecl *FD) {
846 return getPersistentSummary(RetEff: RetEffect::MakeOwned(o: ObjKind::CF),
847 ScratchArgs: ArgEffects(AF.getEmptyMap()));
848}
849
850const RetainSummary *
851RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl *FD) {
852 return getPersistentSummary(RetEff: RetEffect::MakeNotOwned(o: ObjKind::CF),
853 ScratchArgs: ArgEffects(AF.getEmptyMap()),
854 ReceiverEff: ArgEffect(DoNothing), DefaultEff: ArgEffect(DoNothing));
855}
856
857
858
859
860//===----------------------------------------------------------------------===//
861// Summary creation for Selectors.
862//===----------------------------------------------------------------------===//
863
864std::optional<RetEffect>
865RetainSummaryManager::getRetEffectFromAnnotations(QualType RetTy,
866 const Decl *D) {
867 if (hasAnyEnabledAttrOf<NSReturnsRetainedAttr>(D, QT: RetTy))
868 return ObjCAllocRetE;
869
870 if (auto K = hasAnyEnabledAttrOf<CFReturnsRetainedAttr, OSReturnsRetainedAttr,
871 GeneralizedReturnsRetainedAttr>(D, QT: RetTy))
872 return RetEffect::MakeOwned(o: *K);
873
874 if (auto K = hasAnyEnabledAttrOf<
875 CFReturnsNotRetainedAttr, OSReturnsNotRetainedAttr,
876 GeneralizedReturnsNotRetainedAttr, NSReturnsNotRetainedAttr,
877 NSReturnsAutoreleasedAttr>(D, QT: RetTy))
878 return RetEffect::MakeNotOwned(o: *K);
879
880 if (const auto *MD = dyn_cast<CXXMethodDecl>(Val: D))
881 for (const auto *PD : MD->overridden_methods())
882 if (auto RE = getRetEffectFromAnnotations(RetTy, D: PD))
883 return RE;
884
885 return std::nullopt;
886}
887
888/// \return Whether the chain of typedefs starting from @c QT
889/// has a typedef with a given name @c Name.
890static bool hasTypedefNamed(QualType QT,
891 StringRef Name) {
892 while (auto *T = QT->getAs<TypedefType>()) {
893 const auto &Context = T->getDecl()->getASTContext();
894 if (T->getDecl()->getIdentifier() == &Context.Idents.get(Name))
895 return true;
896 QT = T->getDecl()->getUnderlyingType();
897 }
898 return false;
899}
900
901static QualType getCallableReturnType(const NamedDecl *ND) {
902 if (const auto *FD = dyn_cast<FunctionDecl>(Val: ND)) {
903 return FD->getReturnType();
904 } else if (const auto *MD = dyn_cast<ObjCMethodDecl>(Val: ND)) {
905 return MD->getReturnType();
906 } else {
907 llvm_unreachable("Unexpected decl");
908 }
909}
910
911bool RetainSummaryManager::applyParamAnnotationEffect(
912 const ParmVarDecl *pd, unsigned parm_idx, const NamedDecl *FD,
913 RetainSummaryTemplate &Template) {
914 QualType QT = pd->getType();
915 if (auto K =
916 hasAnyEnabledAttrOf<NSConsumedAttr, CFConsumedAttr, OSConsumedAttr,
917 GeneralizedConsumedAttr>(D: pd, QT)) {
918 Template->addArg(af&: AF, idx: parm_idx, e: ArgEffect(DecRef, *K));
919 return true;
920 } else if (auto K = hasAnyEnabledAttrOf<
921 CFReturnsRetainedAttr, OSReturnsRetainedAttr,
922 OSReturnsRetainedOnNonZeroAttr, OSReturnsRetainedOnZeroAttr,
923 GeneralizedReturnsRetainedAttr>(D: pd, QT)) {
924
925 // For OSObjects, we try to guess whether the object is created based
926 // on the return value.
927 if (K == ObjKind::OS) {
928 QualType QT = getCallableReturnType(ND: FD);
929
930 bool HasRetainedOnZero = pd->hasAttr<OSReturnsRetainedOnZeroAttr>();
931 bool HasRetainedOnNonZero = pd->hasAttr<OSReturnsRetainedOnNonZeroAttr>();
932
933 // The usual convention is to create an object on non-zero return, but
934 // it's reverted if the typedef chain has a typedef kern_return_t,
935 // because kReturnSuccess constant is defined as zero.
936 // The convention can be overwritten by custom attributes.
937 bool SuccessOnZero =
938 HasRetainedOnZero ||
939 (hasTypedefNamed(QT, Name: "kern_return_t") && !HasRetainedOnNonZero);
940 bool ShouldSplit = !QT.isNull() && !QT->isVoidType();
941 ArgEffectKind AK = RetainedOutParameter;
942 if (ShouldSplit && SuccessOnZero) {
943 AK = RetainedOutParameterOnZero;
944 } else if (ShouldSplit && (!SuccessOnZero || HasRetainedOnNonZero)) {
945 AK = RetainedOutParameterOnNonZero;
946 }
947 Template->addArg(af&: AF, idx: parm_idx, e: ArgEffect(AK, ObjKind::OS));
948 }
949
950 // For others:
951 // Do nothing. Retained out parameters will either point to a +1 reference
952 // or NULL, but the way you check for failure differs depending on the
953 // API. Consequently, we don't have a good way to track them yet.
954 return true;
955 } else if (auto K = hasAnyEnabledAttrOf<CFReturnsNotRetainedAttr,
956 OSReturnsNotRetainedAttr,
957 GeneralizedReturnsNotRetainedAttr>(
958 D: pd, QT)) {
959 Template->addArg(af&: AF, idx: parm_idx, e: ArgEffect(UnretainedOutParameter, *K));
960 return true;
961 }
962
963 if (const auto *MD = dyn_cast<CXXMethodDecl>(Val: FD)) {
964 for (const auto *OD : MD->overridden_methods()) {
965 const ParmVarDecl *OP = OD->parameters()[parm_idx];
966 if (applyParamAnnotationEffect(pd: OP, parm_idx, FD: OD, Template))
967 return true;
968 }
969 }
970
971 return false;
972}
973
974void
975RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
976 const FunctionDecl *FD) {
977 if (!FD)
978 return;
979
980 assert(Summ && "Must have a summary to add annotations to.");
981 RetainSummaryTemplate Template(Summ, *this);
982
983 // Effects on the parameters.
984 unsigned parm_idx = 0;
985 for (auto pi = FD->param_begin(),
986 pe = FD->param_end(); pi != pe; ++pi, ++parm_idx)
987 applyParamAnnotationEffect(pd: *pi, parm_idx, FD, Template);
988
989 QualType RetTy = FD->getReturnType();
990 if (std::optional<RetEffect> RetE = getRetEffectFromAnnotations(RetTy, D: FD))
991 Template->setRetEffect(*RetE);
992
993 if (hasAnyEnabledAttrOf<OSConsumesThisAttr>(D: FD, QT: RetTy))
994 Template->setThisEffect(ArgEffect(DecRef, ObjKind::OS));
995}
996
997void
998RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
999 const ObjCMethodDecl *MD) {
1000 if (!MD)
1001 return;
1002
1003 assert(Summ && "Must have a valid summary to add annotations to");
1004 RetainSummaryTemplate Template(Summ, *this);
1005
1006 // Effects on the receiver.
1007 if (hasAnyEnabledAttrOf<NSConsumesSelfAttr>(D: MD, QT: MD->getReturnType()))
1008 Template->setReceiverEffect(ArgEffect(DecRef, ObjKind::ObjC));
1009
1010 // Effects on the parameters.
1011 unsigned parm_idx = 0;
1012 for (auto pi = MD->param_begin(), pe = MD->param_end(); pi != pe;
1013 ++pi, ++parm_idx)
1014 applyParamAnnotationEffect(pd: *pi, parm_idx, FD: MD, Template);
1015
1016 QualType RetTy = MD->getReturnType();
1017 if (std::optional<RetEffect> RetE = getRetEffectFromAnnotations(RetTy, D: MD))
1018 Template->setRetEffect(*RetE);
1019}
1020
1021const RetainSummary *
1022RetainSummaryManager::getStandardMethodSummary(const ObjCMethodDecl *MD,
1023 Selector S, QualType RetTy) {
1024 // Any special effects?
1025 ArgEffect ReceiverEff = ArgEffect(DoNothing, ObjKind::ObjC);
1026 RetEffect ResultEff = RetEffect::MakeNoRet();
1027
1028 // Check the method family, and apply any default annotations.
1029 switch (MD ? MD->getMethodFamily() : S.getMethodFamily()) {
1030 case OMF_None:
1031 case OMF_initialize:
1032 case OMF_performSelector:
1033 // Assume all Objective-C methods follow Cocoa Memory Management rules.
1034 // FIXME: Does the non-threaded performSelector family really belong here?
1035 // The selector could be, say, @selector(copy).
1036 if (cocoa::isCocoaObjectRef(T: RetTy))
1037 ResultEff = RetEffect::MakeNotOwned(o: ObjKind::ObjC);
1038 else if (coreFoundation::isCFObjectRef(T: RetTy)) {
1039 // ObjCMethodDecl currently doesn't consider CF objects as valid return
1040 // values for alloc, new, copy, or mutableCopy, so we have to
1041 // double-check with the selector. This is ugly, but there aren't that
1042 // many Objective-C methods that return CF objects, right?
1043 if (MD) {
1044 switch (S.getMethodFamily()) {
1045 case OMF_alloc:
1046 case OMF_new:
1047 case OMF_copy:
1048 case OMF_mutableCopy:
1049 ResultEff = RetEffect::MakeOwned(o: ObjKind::CF);
1050 break;
1051 default:
1052 ResultEff = RetEffect::MakeNotOwned(o: ObjKind::CF);
1053 break;
1054 }
1055 } else {
1056 ResultEff = RetEffect::MakeNotOwned(o: ObjKind::CF);
1057 }
1058 }
1059 break;
1060 case OMF_init:
1061 ResultEff = ObjCInitRetE;
1062 ReceiverEff = ArgEffect(DecRef, ObjKind::ObjC);
1063 break;
1064 case OMF_alloc:
1065 case OMF_new:
1066 case OMF_copy:
1067 case OMF_mutableCopy:
1068 if (cocoa::isCocoaObjectRef(T: RetTy))
1069 ResultEff = ObjCAllocRetE;
1070 else if (coreFoundation::isCFObjectRef(T: RetTy))
1071 ResultEff = RetEffect::MakeOwned(o: ObjKind::CF);
1072 break;
1073 case OMF_autorelease:
1074 ReceiverEff = ArgEffect(Autorelease, ObjKind::ObjC);
1075 break;
1076 case OMF_retain:
1077 ReceiverEff = ArgEffect(IncRef, ObjKind::ObjC);
1078 break;
1079 case OMF_release:
1080 ReceiverEff = ArgEffect(DecRef, ObjKind::ObjC);
1081 break;
1082 case OMF_dealloc:
1083 ReceiverEff = ArgEffect(Dealloc, ObjKind::ObjC);
1084 break;
1085 case OMF_self:
1086 // -self is handled specially by the ExprEngine to propagate the receiver.
1087 break;
1088 case OMF_retainCount:
1089 case OMF_finalize:
1090 // These methods don't return objects.
1091 break;
1092 }
1093
1094 // If one of the arguments in the selector has the keyword 'delegate' we
1095 // should stop tracking the reference count for the receiver. This is
1096 // because the reference count is quite possibly handled by a delegate
1097 // method.
1098 if (S.isKeywordSelector()) {
1099 for (unsigned i = 0, e = S.getNumArgs(); i != e; ++i) {
1100 StringRef Slot = S.getNameForSlot(argIndex: i);
1101 if (Slot.ends_with_insensitive(Suffix: "delegate")) {
1102 if (ResultEff == ObjCInitRetE)
1103 ResultEff = RetEffect::MakeNoRetHard();
1104 else
1105 ReceiverEff = ArgEffect(StopTrackingHard, ObjKind::ObjC);
1106 }
1107 }
1108 }
1109
1110 if (ReceiverEff.getKind() == DoNothing &&
1111 ResultEff.getKind() == RetEffect::NoRet)
1112 return getDefaultSummary();
1113
1114 return getPersistentSummary(RetEff: ResultEff, ScratchArgs: ArgEffects(AF.getEmptyMap()),
1115 ReceiverEff: ArgEffect(ReceiverEff), DefaultEff: ArgEffect(MayEscape));
1116}
1117
1118const RetainSummary *
1119RetainSummaryManager::getClassMethodSummary(const ObjCMessageExpr *ME) {
1120 assert(!ME->isInstanceMessage());
1121 const ObjCInterfaceDecl *Class = ME->getReceiverInterface();
1122
1123 return getMethodSummary(S: ME->getSelector(), ID: Class, MD: ME->getMethodDecl(),
1124 RetTy: ME->getType(), CachedSummaries&: ObjCClassMethodSummaries);
1125}
1126
1127const RetainSummary *RetainSummaryManager::getInstanceMethodSummary(
1128 const ObjCMessageExpr *ME,
1129 QualType ReceiverType) {
1130 const ObjCInterfaceDecl *ReceiverClass = nullptr;
1131
1132 // We do better tracking of the type of the object than the core ExprEngine.
1133 // See if we have its type in our private state.
1134 if (!ReceiverType.isNull())
1135 if (const auto *PT = ReceiverType->getAs<ObjCObjectPointerType>())
1136 ReceiverClass = PT->getInterfaceDecl();
1137
1138 // If we don't know what kind of object this is, fall back to its static type.
1139 if (!ReceiverClass)
1140 ReceiverClass = ME->getReceiverInterface();
1141
1142 // FIXME: The receiver could be a reference to a class, meaning that
1143 // we should use the class method.
1144 // id x = [NSObject class];
1145 // [x performSelector:... withObject:... afterDelay:...];
1146 Selector S = ME->getSelector();
1147 const ObjCMethodDecl *Method = ME->getMethodDecl();
1148 if (!Method && ReceiverClass)
1149 Method = ReceiverClass->getInstanceMethod(Sel: S);
1150
1151 return getMethodSummary(S, ID: ReceiverClass, MD: Method, RetTy: ME->getType(),
1152 CachedSummaries&: ObjCMethodSummaries);
1153}
1154
1155const RetainSummary *
1156RetainSummaryManager::getMethodSummary(Selector S,
1157 const ObjCInterfaceDecl *ID,
1158 const ObjCMethodDecl *MD, QualType RetTy,
1159 ObjCMethodSummariesTy &CachedSummaries) {
1160
1161 // Objective-C method summaries are only applicable to ObjC and CF objects.
1162 if (!TrackObjCAndCFObjects)
1163 return getDefaultSummary();
1164
1165 // Look up a summary in our summary cache.
1166 const RetainSummary *Summ = CachedSummaries.find(D: ID, S);
1167
1168 if (!Summ) {
1169 Summ = getStandardMethodSummary(MD, S, RetTy);
1170
1171 // Annotations override defaults.
1172 updateSummaryFromAnnotations(Summ, MD);
1173
1174 // Memoize the summary.
1175 CachedSummaries[ObjCSummaryKey(ID, S)] = Summ;
1176 }
1177
1178 return Summ;
1179}
1180
1181void RetainSummaryManager::InitializeClassMethodSummaries() {
1182 ArgEffects ScratchArgs = AF.getEmptyMap();
1183
1184 // Create the [NSAssertionHandler currentHander] summary.
1185 addClassMethSummary(Cls: "NSAssertionHandler", name: "currentHandler",
1186 Summ: getPersistentSummary(RetEff: RetEffect::MakeNotOwned(o: ObjKind::ObjC),
1187 ScratchArgs));
1188
1189 // Create the [NSAutoreleasePool addObject:] summary.
1190 ScratchArgs = AF.add(Old: ScratchArgs, K: 0, D: ArgEffect(Autorelease));
1191 addClassMethSummary(Cls: "NSAutoreleasePool", name: "addObject",
1192 Summ: getPersistentSummary(RetEff: RetEffect::MakeNoRet(), ScratchArgs,
1193 ReceiverEff: ArgEffect(DoNothing),
1194 DefaultEff: ArgEffect(Autorelease)));
1195}
1196
1197void RetainSummaryManager::InitializeMethodSummaries() {
1198
1199 ArgEffects ScratchArgs = AF.getEmptyMap();
1200 // Create the "init" selector. It just acts as a pass-through for the
1201 // receiver.
1202 const RetainSummary *InitSumm = getPersistentSummary(
1203 RetEff: ObjCInitRetE, ScratchArgs, ReceiverEff: ArgEffect(DecRef, ObjKind::ObjC));
1204 addNSObjectMethSummary(S: GetNullarySelector(name: "init", Ctx), Summ: InitSumm);
1205
1206 // awakeAfterUsingCoder: behaves basically like an 'init' method. It
1207 // claims the receiver and returns a retained object.
1208 addNSObjectMethSummary(S: GetUnarySelector(name: "awakeAfterUsingCoder", Ctx),
1209 Summ: InitSumm);
1210
1211 // The next methods are allocators.
1212 const RetainSummary *AllocSumm = getPersistentSummary(RetEff: ObjCAllocRetE,
1213 ScratchArgs);
1214 const RetainSummary *CFAllocSumm =
1215 getPersistentSummary(RetEff: RetEffect::MakeOwned(o: ObjKind::CF), ScratchArgs);
1216
1217 // Create the "retain" selector.
1218 RetEffect NoRet = RetEffect::MakeNoRet();
1219 const RetainSummary *Summ = getPersistentSummary(
1220 RetEff: NoRet, ScratchArgs, ReceiverEff: ArgEffect(IncRef, ObjKind::ObjC));
1221 addNSObjectMethSummary(S: GetNullarySelector(name: "retain", Ctx), Summ);
1222
1223 // Create the "release" selector.
1224 Summ = getPersistentSummary(RetEff: NoRet, ScratchArgs,
1225 ReceiverEff: ArgEffect(DecRef, ObjKind::ObjC));
1226 addNSObjectMethSummary(S: GetNullarySelector(name: "release", Ctx), Summ);
1227
1228 // Create the -dealloc summary.
1229 Summ = getPersistentSummary(RetEff: NoRet, ScratchArgs, ReceiverEff: ArgEffect(Dealloc,
1230 ObjKind::ObjC));
1231 addNSObjectMethSummary(S: GetNullarySelector(name: "dealloc", Ctx), Summ);
1232
1233 // Create the "autorelease" selector.
1234 Summ = getPersistentSummary(RetEff: NoRet, ScratchArgs, ReceiverEff: ArgEffect(Autorelease,
1235 ObjKind::ObjC));
1236 addNSObjectMethSummary(S: GetNullarySelector(name: "autorelease", Ctx), Summ);
1237
1238 // For NSWindow, allocated objects are (initially) self-owned.
1239 // FIXME: For now we opt for false negatives with NSWindow, as these objects
1240 // self-own themselves. However, they only do this once they are displayed.
1241 // Thus, we need to track an NSWindow's display status.
1242 const RetainSummary *NoTrackYet =
1243 getPersistentSummary(RetEff: RetEffect::MakeNoRet(), ScratchArgs,
1244 ReceiverEff: ArgEffect(StopTracking), DefaultEff: ArgEffect(StopTracking));
1245
1246 addClassMethSummary(Cls: "NSWindow", name: "alloc", Summ: NoTrackYet);
1247
1248 // For NSPanel (which subclasses NSWindow), allocated objects are not
1249 // self-owned.
1250 // FIXME: For now we don't track NSPanels. object for the same reason
1251 // as for NSWindow objects.
1252 addClassMethSummary(Cls: "NSPanel", name: "alloc", Summ: NoTrackYet);
1253
1254 // For NSNull, objects returned by +null are singletons that ignore
1255 // retain/release semantics. Just don't track them.
1256 addClassMethSummary(Cls: "NSNull", name: "null", Summ: NoTrackYet);
1257
1258 // Don't track allocated autorelease pools, as it is okay to prematurely
1259 // exit a method.
1260 addClassMethSummary(Cls: "NSAutoreleasePool", name: "alloc", Summ: NoTrackYet);
1261 addClassMethSummary(Cls: "NSAutoreleasePool", name: "allocWithZone", Summ: NoTrackYet, isNullary: false);
1262 addClassMethSummary(Cls: "NSAutoreleasePool", name: "new", Summ: NoTrackYet);
1263
1264 // Create summaries QCRenderer/QCView -createSnapShotImageOfType:
1265 addInstMethSummary(Cls: "QCRenderer", Summ: AllocSumm, Kws: "createSnapshotImageOfType");
1266 addInstMethSummary(Cls: "QCView", Summ: AllocSumm, Kws: "createSnapshotImageOfType");
1267
1268 // Create summaries for CIContext, 'createCGImage' and
1269 // 'createCGLayerWithSize'. These objects are CF objects, and are not
1270 // automatically garbage collected.
1271 addInstMethSummary(Cls: "CIContext", Summ: CFAllocSumm, Kws: "createCGImage", Kws: "fromRect");
1272 addInstMethSummary(Cls: "CIContext", Summ: CFAllocSumm, Kws: "createCGImage", Kws: "fromRect",
1273 Kws: "format", Kws: "colorSpace");
1274 addInstMethSummary(Cls: "CIContext", Summ: CFAllocSumm, Kws: "createCGLayerWithSize", Kws: "info");
1275}
1276
1277const RetainSummary *
1278RetainSummaryManager::getMethodSummary(const ObjCMethodDecl *MD) {
1279 const ObjCInterfaceDecl *ID = MD->getClassInterface();
1280 Selector S = MD->getSelector();
1281 QualType ResultTy = MD->getReturnType();
1282
1283 ObjCMethodSummariesTy *CachedSummaries;
1284 if (MD->isInstanceMethod())
1285 CachedSummaries = &ObjCMethodSummaries;
1286 else
1287 CachedSummaries = &ObjCClassMethodSummaries;
1288
1289 return getMethodSummary(S, ID, MD, RetTy: ResultTy, CachedSummaries&: *CachedSummaries);
1290}
1291