#pragma once #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #endif //=== RetainSummaryManager.h - Summaries for reference counting ---*- C++ -*--// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines summaries implementation for retain counting, which // implements a reference count checker for Core Foundation and Cocoa // on (Mac OS X). // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_ANALYSIS_RETAINSUMMARYMANAGER_H #define LLVM_CLANG_ANALYSIS_RETAINSUMMARYMANAGER_H #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableMap.h" #include "clang/AST/Attr.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/ParentMap.h" #include "clang/Analysis/AnyCall.h" #include "clang/Analysis/SelectorExtras.h" #include "llvm/ADT/STLExtras.h" using namespace clang; namespace clang { namespace ento { /// Determines the object kind of a tracked object. enum class ObjKind { /// Indicates that the tracked object is a CF object. CF, /// Indicates that the tracked object is an Objective-C object. ObjC, /// Indicates that the tracked object could be a CF or Objective-C object. AnyObj, /// Indicates that the tracked object is a generalized object. Generalized, /// Indicates that the tracking object is a descendant of a /// referenced-counted OSObject, used in the Darwin kernel. OS }; enum ArgEffectKind { /// There is no effect. DoNothing, /// The argument is treated as if an -autorelease message had been sent to /// the referenced object. Autorelease, /// The argument is treated as if the referenced object was deallocated. Dealloc, /// The argument has its reference count decreased by 1. DecRef, /// The argument has its reference count decreased by 1 to model /// a transferred bridge cast under ARC. DecRefBridgedTransferred, /// The argument has its reference count increased by 1. IncRef, /// The argument is a pointer to a retain-counted object; on exit, the new /// value of the pointer is a +0 value. UnretainedOutParameter, /// The argument is a pointer to a retain-counted object; on exit, the new /// value of the pointer is a +1 value. RetainedOutParameter, /// The argument is a pointer to a retain-counted object; on exit, the new /// value of the pointer is a +1 value iff the return code is zero. RetainedOutParameterOnZero, /// The argument is a pointer to a retain-counted object; on exit, the new /// value of the pointer is a +1 value iff the return code is non-zero. RetainedOutParameterOnNonZero, /// The argument is treated as potentially escaping, meaning that /// even when its reference count hits 0 it should be treated as still /// possibly being alive as someone else *may* be holding onto the object. MayEscape, /// All typestate tracking of the object ceases. This is usually employed /// when the effect of the call is completely unknown. StopTracking, /// All typestate tracking of the object ceases. Unlike StopTracking, /// this is also enforced when the method body is inlined. /// /// In some cases, we obtain a better summary for this checker /// by looking at the call site than by inlining the function. /// Signifies that we should stop tracking the symbol even if /// the function is inlined. StopTrackingHard, /// Performs the combined functionality of DecRef and StopTrackingHard. /// /// The models the effect that the called function decrements the reference /// count of the argument and all typestate tracking on that argument /// should cease. DecRefAndStopTrackingHard, }; /// An ArgEffect summarizes the retain count behavior on an argument or receiver /// to a function or method. class ArgEffect { ArgEffectKind K; ObjKind O; public: explicit ArgEffect(ArgEffectKind K = DoNothing, ObjKind O = ObjKind::AnyObj) : K(K), O(O) {} ArgEffectKind getKind() const { return K; } ObjKind getObjKind() const { return O; } ArgEffect withKind(ArgEffectKind NewK) { return ArgEffect(NewK, O); } bool operator==(const ArgEffect &Other) const { return K == Other.K && O == Other.O; } }; /// RetEffect summarizes a call's retain/release behavior with respect /// to its return value. class RetEffect { public: enum Kind { /// Indicates that no retain count information is tracked for /// the return value. NoRet, /// Indicates that the returned value is an owned (+1) symbol. OwnedSymbol, /// Indicates that the returned value is an object with retain count /// semantics but that it is not owned (+0). This is the default /// for getters, etc. NotOwnedSymbol, /// Indicates that the return value is an owned object when the /// receiver is also a tracked object. OwnedWhenTrackedReceiver, // Treat this function as returning a non-tracked symbol even if // the function has been inlined. This is used where the call // site summary is more precise than the summary indirectly produced // by inlining the function NoRetHard }; private: Kind K; ObjKind O; RetEffect(Kind k, ObjKind o = ObjKind::AnyObj) : K(k), O(o) {} public: Kind getKind() const { return K; } ObjKind getObjKind() const { return O; } bool isOwned() const { return K == OwnedSymbol || K == OwnedWhenTrackedReceiver; } bool notOwned() const { return K == NotOwnedSymbol; } bool operator==(const RetEffect &Other) const { return K == Other.K && O == Other.O; } static RetEffect MakeOwnedWhenTrackedReceiver() { return RetEffect(OwnedWhenTrackedReceiver, ObjKind::ObjC); } static RetEffect MakeOwned(ObjKind o) { return RetEffect(OwnedSymbol, o); } static RetEffect MakeNotOwned(ObjKind o) { return RetEffect(NotOwnedSymbol, o); } static RetEffect MakeNoRet() { return RetEffect(NoRet); } static RetEffect MakeNoRetHard() { return RetEffect(NoRetHard); } }; /// A key identifying a summary. class ObjCSummaryKey { IdentifierInfo* II; Selector S; public: ObjCSummaryKey(IdentifierInfo* ii, Selector s) : II(ii), S(s) {} ObjCSummaryKey(const ObjCInterfaceDecl *d, Selector s) : II(d ? d->getIdentifier() : nullptr), S(s) {} ObjCSummaryKey(Selector s) : II(nullptr), S(s) {} IdentifierInfo *getIdentifier() const { return II; } Selector getSelector() const { return S; } }; } // end namespace ento } // end namespace clang using namespace ento; namespace llvm { //===----------------------------------------------------------------------===// // Adapters for FoldingSet. //===----------------------------------------------------------------------===// template <> struct FoldingSetTrait { static inline void Profile(const ArgEffect X, FoldingSetNodeID &ID) { ID.AddInteger((unsigned) X.getKind()); ID.AddInteger((unsigned) X.getObjKind()); } }; template <> struct FoldingSetTrait { static inline void Profile(const RetEffect &X, FoldingSetNodeID &ID) { ID.AddInteger((unsigned) X.getKind()); ID.AddInteger((unsigned) X.getObjKind()); } }; template <> struct DenseMapInfo { static inline ObjCSummaryKey getEmptyKey() { return ObjCSummaryKey(DenseMapInfo::getEmptyKey(), DenseMapInfo::getEmptyKey()); } static inline ObjCSummaryKey getTombstoneKey() { return ObjCSummaryKey(DenseMapInfo::getTombstoneKey(), DenseMapInfo::getTombstoneKey()); } static unsigned getHashValue(const ObjCSummaryKey &V) { typedef std::pair PairTy; return DenseMapInfo::getHashValue(PairTy(V.getIdentifier(), V.getSelector())); } static bool isEqual(const ObjCSummaryKey& LHS, const ObjCSummaryKey& RHS) { return LHS.getIdentifier() == RHS.getIdentifier() && LHS.getSelector() == RHS.getSelector(); } }; } // end llvm namespace namespace clang { namespace ento { /// ArgEffects summarizes the effects of a function/method call on all of /// its arguments. typedef llvm::ImmutableMap ArgEffects; /// Summary for a function with respect to ownership changes. class RetainSummary { /// Args - a map of (index, ArgEffect) pairs, where index /// specifies the argument (starting from 0). This can be sparsely /// populated; arguments with no entry in Args use 'DefaultArgEffect'. ArgEffects Args; /// DefaultArgEffect - The default ArgEffect to apply to arguments that /// do not have an entry in Args. ArgEffect DefaultArgEffect; /// Receiver - If this summary applies to an Objective-C message expression, /// this is the effect applied to the state of the receiver. ArgEffect Receiver; /// Effect on "this" pointer - applicable only to C++ method calls. ArgEffect This; /// Ret - The effect on the return value. Used to indicate if the /// function/method call returns a new tracked symbol. RetEffect Ret; public: RetainSummary(ArgEffects A, RetEffect R, ArgEffect defaultEff, ArgEffect ReceiverEff, ArgEffect ThisEff) : Args(A), DefaultArgEffect(defaultEff), Receiver(ReceiverEff), This(ThisEff), Ret(R) {} /// getArg - Return the argument effect on the argument specified by /// idx (starting from 0). ArgEffect getArg(unsigned idx) const { if (const ArgEffect *AE = Args.lookup(idx)) return *AE; return DefaultArgEffect; } void addArg(ArgEffects::Factory &af, unsigned idx, ArgEffect e) { Args = af.add(Args, idx, e); } /// setDefaultArgEffect - Set the default argument effect. void setDefaultArgEffect(ArgEffect E) { DefaultArgEffect = E; } /// getRetEffect - Returns the effect on the return value of the call. RetEffect getRetEffect() const { return Ret; } /// setRetEffect - Set the effect of the return value of the call. void setRetEffect(RetEffect E) { Ret = E; } /// Sets the effect on the receiver of the message. void setReceiverEffect(ArgEffect e) { Receiver = e; } /// getReceiverEffect - Returns the effect on the receiver of the call. /// This is only meaningful if the summary applies to an ObjCMessageExpr*. ArgEffect getReceiverEffect() const { return Receiver; } /// \return the effect on the "this" receiver of the method call. /// This is only meaningful if the summary applies to CXXMethodDecl*. ArgEffect getThisEffect() const { return This; } ArgEffect getDefaultEffect() const { return DefaultArgEffect; } /// Set the effect of the method on "this". void setThisEffect(ArgEffect e) { This = e; } bool isNoop() const { return Ret == RetEffect::MakeNoRet() && Receiver.getKind() == DoNothing && DefaultArgEffect.getKind() == MayEscape && This.getKind() == DoNothing && Args.isEmpty(); } /// Test if two retain summaries are identical. Note that merely equivalent /// summaries are not necessarily identical (for example, if an explicit /// argument effect matches the default effect). bool operator==(const RetainSummary &Other) const { return Args == Other.Args && DefaultArgEffect == Other.DefaultArgEffect && Receiver == Other.Receiver && This == Other.This && Ret == Other.Ret; } /// Profile this summary for inclusion in a FoldingSet. void Profile(llvm::FoldingSetNodeID& ID) const { ID.Add(Args); ID.Add(DefaultArgEffect); ID.Add(Receiver); ID.Add(This); ID.Add(Ret); } /// A retain summary is simple if it has no ArgEffects other than the default. bool isSimple() const { return Args.isEmpty(); } ArgEffects getArgEffects() const { return Args; } private: ArgEffect getDefaultArgEffect() const { return DefaultArgEffect; } friend class RetainSummaryManager; }; class ObjCSummaryCache { typedef llvm::DenseMap MapTy; MapTy M; public: ObjCSummaryCache() {} const RetainSummary * find(const ObjCInterfaceDecl *D, Selector S) { // Do a lookup with the (D,S) pair. If we find a match return // the iterator. ObjCSummaryKey K(D, S); MapTy::iterator I = M.find(K); if (I != M.end()) return I->second; if (!D) return nullptr; // Walk the super chain. If we find a hit with a parent, we'll end // up returning that summary. We actually allow that key (null,S), as // we cache summaries for the null ObjCInterfaceDecl* to allow us to // generate initial summaries without having to worry about NSObject // being declared. // FIXME: We may change this at some point. for (ObjCInterfaceDecl *C=D->getSuperClass() ;; C=C->getSuperClass()) { if ((I = M.find(ObjCSummaryKey(C, S))) != M.end()) break; if (!C) return nullptr; } // Cache the summary with original key to make the next lookup faster // and return the iterator. const RetainSummary *Summ = I->second; M[K] = Summ; return Summ; } const RetainSummary *find(IdentifierInfo* II, Selector S) { // FIXME: Class method lookup. Right now we don't have a good way // of going between IdentifierInfo* and the class hierarchy. MapTy::iterator I = M.find(ObjCSummaryKey(II, S)); if (I == M.end()) I = M.find(ObjCSummaryKey(S)); return I == M.end() ? nullptr : I->second; } const RetainSummary *& operator[](ObjCSummaryKey K) { return M[K]; } const RetainSummary *& operator[](Selector S) { return M[ ObjCSummaryKey(S) ]; } }; class RetainSummaryTemplate; class RetainSummaryManager { typedef llvm::DenseMap FuncSummariesTy; typedef ObjCSummaryCache ObjCMethodSummariesTy; typedef llvm::FoldingSetNodeWrapper CachedSummaryNode; /// Ctx - The ASTContext object for the analyzed ASTs. ASTContext &Ctx; /// Records whether or not the analyzed code runs in ARC mode. const bool ARCEnabled; /// Track Objective-C and CoreFoundation objects. const bool TrackObjCAndCFObjects; /// Track sublcasses of OSObject. const bool TrackOSObjects; /// FuncSummaries - A map from FunctionDecls to summaries. FuncSummariesTy FuncSummaries; /// ObjCClassMethodSummaries - A map from selectors (for instance methods) /// to summaries. ObjCMethodSummariesTy ObjCClassMethodSummaries; /// ObjCMethodSummaries - A map from selectors to summaries. ObjCMethodSummariesTy ObjCMethodSummaries; /// BPAlloc - A BumpPtrAllocator used for allocating summaries, ArgEffects, /// and all other data used by the checker. llvm::BumpPtrAllocator BPAlloc; /// AF - A factory for ArgEffects objects. ArgEffects::Factory AF; /// ObjCAllocRetE - Default return effect for methods returning Objective-C /// objects. RetEffect ObjCAllocRetE; /// ObjCInitRetE - Default return effect for init methods returning /// Objective-C objects. RetEffect ObjCInitRetE; /// SimpleSummaries - Used for uniquing summaries that don't have special /// effects. llvm::FoldingSet SimpleSummaries; /// Create an OS object at +1. const RetainSummary *getOSSummaryCreateRule(const FunctionDecl *FD); /// Get an OS object at +0. const RetainSummary *getOSSummaryGetRule(const FunctionDecl *FD); /// Increment the reference count on OS object. const RetainSummary *getOSSummaryRetainRule(const FunctionDecl *FD); /// Decrement the reference count on OS object. const RetainSummary *getOSSummaryReleaseRule(const FunctionDecl *FD); /// Free the OS object. const RetainSummary *getOSSummaryFreeRule(const FunctionDecl *FD); const RetainSummary *getUnarySummary(const FunctionType* FT, ArgEffectKind AE); const RetainSummary *getCFSummaryCreateRule(const FunctionDecl *FD); const RetainSummary *getCFSummaryGetRule(const FunctionDecl *FD); const RetainSummary *getCFCreateGetRuleSummary(const FunctionDecl *FD); const RetainSummary *getPersistentSummary(const RetainSummary &OldSumm); const RetainSummary * getPersistentSummary(RetEffect RetEff, ArgEffects ScratchArgs, ArgEffect ReceiverEff = ArgEffect(DoNothing), ArgEffect DefaultEff = ArgEffect(MayEscape), ArgEffect ThisEff = ArgEffect(DoNothing)) { RetainSummary Summ(ScratchArgs, RetEff, DefaultEff, ReceiverEff, ThisEff); return getPersistentSummary(Summ); } const RetainSummary *getDoNothingSummary() { return getPersistentSummary(RetEffect::MakeNoRet(), ArgEffects(AF.getEmptyMap()), ArgEffect(DoNothing), ArgEffect(DoNothing)); } const RetainSummary *getDefaultSummary() { return getPersistentSummary(RetEffect::MakeNoRet(), ArgEffects(AF.getEmptyMap()), ArgEffect(DoNothing), ArgEffect(MayEscape)); } const RetainSummary *getPersistentStopSummary() { return getPersistentSummary( RetEffect::MakeNoRet(), ArgEffects(AF.getEmptyMap()), ArgEffect(StopTracking), ArgEffect(StopTracking)); } void InitializeClassMethodSummaries(); void InitializeMethodSummaries(); void addNSObjectClsMethSummary(Selector S, const RetainSummary *Summ) { ObjCClassMethodSummaries[S] = Summ; } void addNSObjectMethSummary(Selector S, const RetainSummary *Summ) { ObjCMethodSummaries[S] = Summ; } void addClassMethSummary(const char* Cls, const char* name, const RetainSummary *Summ, bool isNullary = true) { IdentifierInfo* ClsII = &Ctx.Idents.get(Cls); Selector S = isNullary ? GetNullarySelector(name, Ctx) : GetUnarySelector(name, Ctx); ObjCClassMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ; } void addInstMethSummary(const char* Cls, const char* nullaryName, const RetainSummary *Summ) { IdentifierInfo* ClsII = &Ctx.Idents.get(Cls); Selector S = GetNullarySelector(nullaryName, Ctx); ObjCMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ; } template void addMethodSummary(IdentifierInfo *ClsII, ObjCMethodSummariesTy &Summaries, const RetainSummary *Summ, Keywords *... Kws) { Selector S = getKeywordSelector(Ctx, Kws...); Summaries[ObjCSummaryKey(ClsII, S)] = Summ; } template void addInstMethSummary(const char *Cls, const RetainSummary *Summ, Keywords *... Kws) { addMethodSummary(&Ctx.Idents.get(Cls), ObjCMethodSummaries, Summ, Kws...); } template void addClsMethSummary(const char *Cls, const RetainSummary *Summ, Keywords *... Kws) { addMethodSummary(&Ctx.Idents.get(Cls), ObjCClassMethodSummaries, Summ, Kws...); } template void addClsMethSummary(IdentifierInfo *II, const RetainSummary *Summ, Keywords *... Kws) { addMethodSummary(II, ObjCClassMethodSummaries, Summ, Kws...); } const RetainSummary * generateSummary(const FunctionDecl *FD, bool &AllowAnnotations); /// Return a summary for OSObject, or nullptr if not found. const RetainSummary *getSummaryForOSObject(const FunctionDecl *FD, StringRef FName, QualType RetTy); /// Return a summary for Objective-C or CF object, or nullptr if not found. const RetainSummary *getSummaryForObjCOrCFObject( const FunctionDecl *FD, StringRef FName, QualType RetTy, const FunctionType *FT, bool &AllowAnnotations); /// Apply the annotation of @c pd in function @c FD /// to the resulting summary stored in out-parameter @c Template. /// \return whether an annotation was applied. bool applyParamAnnotationEffect(const ParmVarDecl *pd, unsigned parm_idx, const NamedDecl *FD, RetainSummaryTemplate &Template); public: RetainSummaryManager(ASTContext &ctx, bool trackObjCAndCFObjects, bool trackOSObjects) : Ctx(ctx), ARCEnabled((bool)Ctx.getLangOpts().ObjCAutoRefCount), TrackObjCAndCFObjects(trackObjCAndCFObjects), TrackOSObjects(trackOSObjects), AF(BPAlloc), ObjCAllocRetE(ARCEnabled ? RetEffect::MakeNotOwned(ObjKind::ObjC) : RetEffect::MakeOwned(ObjKind::ObjC)), ObjCInitRetE(ARCEnabled ? RetEffect::MakeNotOwned(ObjKind::ObjC) : RetEffect::MakeOwnedWhenTrackedReceiver()) { InitializeClassMethodSummaries(); InitializeMethodSummaries(); } enum class BehaviorSummary { // Function does not return. NoOp, // Function returns the first argument. Identity, // Function returns "this" argument. IdentityThis, // Function either returns zero, or the input parameter. IdentityOrZero }; Optional canEval(const CallExpr *CE, const FunctionDecl *FD, bool &hasTrustedImplementationAnnotation); /// \return Whether the type corresponds to a known smart pointer /// implementation (that is, everything about it is inlineable). static bool isKnownSmartPointer(QualType QT); bool isTrustedReferenceCountImplementation(const Decl *FD); const RetainSummary *getSummary(AnyCall C, bool HasNonZeroCallbackArg=false, bool IsReceiverUnconsumedSelf=false, QualType ReceiverType={}); RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; } private: /// getMethodSummary - This version of getMethodSummary is used to query /// the summary for the current method being analyzed. const RetainSummary *getMethodSummary(const ObjCMethodDecl *MD); const RetainSummary *getFunctionSummary(const FunctionDecl *FD); const RetainSummary *getMethodSummary(Selector S, const ObjCInterfaceDecl *ID, const ObjCMethodDecl *MD, QualType RetTy, ObjCMethodSummariesTy &CachedSummaries); const RetainSummary * getInstanceMethodSummary(const ObjCMessageExpr *ME, QualType ReceiverType); const RetainSummary *getClassMethodSummary(const ObjCMessageExpr *ME); const RetainSummary *getStandardMethodSummary(const ObjCMethodDecl *MD, Selector S, QualType RetTy); /// Determine if there is a special return effect for this function or method. Optional getRetEffectFromAnnotations(QualType RetTy, const Decl *D); void updateSummaryFromAnnotations(const RetainSummary *&Summ, const ObjCMethodDecl *MD); void updateSummaryFromAnnotations(const RetainSummary *&Summ, const FunctionDecl *FD); const RetainSummary *updateSummaryForNonZeroCallbackArg(const RetainSummary *S, AnyCall &C); /// Special case '[super init];' and '[self init];' /// /// Even though calling '[super init]' without assigning the result to self /// and checking if the parent returns 'nil' is a bad pattern, it is common. /// Additionally, our Self Init checker already warns about it. To avoid /// overwhelming the user with messages from both checkers, we model the case /// of '[super init]' in cases when it is not consumed by another expression /// as if the call preserves the value of 'self'; essentially, assuming it can /// never fail and return 'nil'. /// Note, we don't want to just stop tracking the value since we want the /// RetainCount checker to report leaks and use-after-free if SelfInit checker /// is turned off. void updateSummaryForReceiverUnconsumedSelf(const RetainSummary *&S); /// Set argument types for arguments which are not doing anything. void updateSummaryForArgumentTypes(const AnyCall &C, const RetainSummary *&RS); /// Determine whether a declaration @c D of correspondent type (return /// type for functions/methods) @c QT has any of the given attributes, /// provided they pass necessary validation checks AND tracking the given /// attribute is enabled. /// Returns the object kind corresponding to the present attribute, or None, /// if none of the specified attributes are present. /// Crashes if passed an attribute which is not explicitly handled. template Optional hasAnyEnabledAttrOf(const Decl *D, QualType QT); template Optional hasAnyEnabledAttrOf(const Decl *D, QualType QT); friend class RetainSummaryTemplate; }; // Used to avoid allocating long-term (BPAlloc'd) memory for default retain // summaries. If a function or method looks like it has a default summary, but // it has annotations, the annotations are added to the stack-based template // and then copied into managed memory. class RetainSummaryTemplate { RetainSummaryManager &Manager; const RetainSummary *&RealSummary; RetainSummary ScratchSummary; bool Accessed; public: RetainSummaryTemplate(const RetainSummary *&real, RetainSummaryManager &mgr) : Manager(mgr), RealSummary(real), ScratchSummary(*real), Accessed(false) {} ~RetainSummaryTemplate() { if (Accessed) RealSummary = Manager.getPersistentSummary(ScratchSummary); } RetainSummary &operator*() { Accessed = true; return ScratchSummary; } RetainSummary *operator->() { Accessed = true; return &ScratchSummary; } }; } // end namespace ento } // end namespace clang #endif #ifdef __GNUC__ #pragma GCC diagnostic pop #endif