From: Jordan Rose Date: Mon, 2 Jul 2012 19:27:51 +0000 (+0000) Subject: [analyzer] Move the last bits of CallOrObjCMessage over to CallEvent. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=85d7e01cf639b257d70f8a129709a2d7594d7b22;p=clang [analyzer] Move the last bits of CallOrObjCMessage over to CallEvent. This involved refactoring some common pointer-escapes code onto CallEvent, then having MallocChecker use those callbacks for whether or not to consider a pointer's /ownership/ as escaping. This still needs to be pinned down, and probably we want to make the new argumentsMayEscape() function a little more discerning (content invalidation vs. ownership/metadata invalidation), but this is a good improvement. As a bonus, also remove CallOrObjCMessage from the source completely. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@159557 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h b/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h index 45b5fc70a2..1327c8971c 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h @@ -132,6 +132,15 @@ public: /// \brief Returns true if any of the arguments appear to represent callbacks. bool hasNonZeroCallbackArg() const; + /// \brief Returns true if any of the arguments are known to escape to long- + /// term storage, even if this method will not modify them. + // NOTE: The exact semantics of this are still being defined! + // We don't really want a list of hardcoded exceptions in the long run, + // but we don't want duplicated lists of known APIs in the short term either. + virtual bool argumentsMayEscape() const { + return hasNonZeroCallbackArg(); + } + /// \brief Returns a new state with all argument regions invalidated. /// /// This accepts an alternate state in case some processing has already @@ -139,6 +148,10 @@ public: ProgramStateRef invalidateRegions(unsigned BlockCount, ProgramStateRef Orig = 0) const; + /// \brief Returns true if this is a statement that can be considered for + /// inlining. + static bool mayBeInlined(const Stmt *S); + // Iterator access to parameter types. private: typedef std::const_mem_fun_t get_type_fun; @@ -173,6 +186,8 @@ protected: public: virtual const FunctionDecl *getDecl() const = 0; + bool argumentsMayEscape() const; + static bool classof(const CallEvent *CA) { return CA->getKind() >= CE_BEG_FUNCTION_CALLS && CA->getKind() <= CE_END_FUNCTION_CALLS; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h index 8fc4050c7e..e567d2d498 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h @@ -157,143 +157,6 @@ public: } }; -/// \brief Common wrapper for a call expression, ObjC message, or C++ -/// constructor, mainly to provide a common interface for their arguments. -class CallOrObjCMessage { - llvm::PointerUnion CallE; - ObjCMessage Msg; - ProgramStateRef State; - const LocationContext *LCtx; - - bool isCallbackArg(unsigned Idx, const Type *T) const; - -public: - CallOrObjCMessage(const CallExpr *callE, ProgramStateRef state, - const LocationContext *lctx) - : CallE(callE), State(state), LCtx(lctx) {} - CallOrObjCMessage(const CXXConstructExpr *consE, ProgramStateRef state, - const LocationContext *lctx) - : CallE(consE), State(state), LCtx(lctx) {} - CallOrObjCMessage(const ObjCMessage &msg, ProgramStateRef state, - const LocationContext *lctx) - : CallE((CallExpr *)0), Msg(msg), State(state), LCtx(lctx) {} - - QualType getResultType(ASTContext &ctx) const; - - bool isFunctionCall() const { - return CallE && CallE.is(); - } - - bool isCXXConstructExpr() const { - return CallE && CallE.is(); - } - - bool isObjCMessage() const { - return !CallE; - } - - bool isCXXCall() const { - const CallExpr *ActualCallE = CallE.dyn_cast(); - return ActualCallE && isa(ActualCallE); - } - - /// Check if the callee is declared in the system header. - bool isInSystemHeader() const { - if (const Decl *D = getDecl()) { - const SourceManager &SM = - State->getStateManager().getContext().getSourceManager(); - SourceLocation Loc = D->getLocation(); - // Be careful: the implicit declarations of operator new/delete have - // invalid source locations but should still count as system files. - if (Loc.isValid()) - return SM.isInSystemHeader(D->getLocation()); - else if (const FunctionDecl *FD = dyn_cast(D)) - return FD->isOverloadedOperator() && FD->isImplicit() && FD->isGlobal(); - } - return false; - } - - const Expr *getOriginExpr() const { - if (!CallE) - return Msg.getMessageExpr(); - if (const CXXConstructExpr *Ctor = - CallE.dyn_cast()) - return Ctor; - return CallE.get(); - } - - SVal getFunctionCallee() const; - SVal getCXXCallee() const; - SVal getInstanceMessageReceiver(const LocationContext *LC) const; - - /// Get the declaration of the function or method. - const Decl *getDecl() const; - - unsigned getNumArgs() const { - if (!CallE) - return Msg.getNumArgs(); - if (const CXXConstructExpr *Ctor = - CallE.dyn_cast()) - return Ctor->getNumArgs(); - return CallE.get()->getNumArgs(); - } - - SVal getArgSVal(unsigned i) const { - assert(i < getNumArgs()); - if (!CallE) - return Msg.getArgSVal(i, LCtx, State); - return State->getSVal(getArg(i), LCtx); - } - - const Expr *getArg(unsigned i) const { - assert(i < getNumArgs()); - if (!CallE) - return Msg.getArgExpr(i); - if (const CXXConstructExpr *Ctor = - CallE.dyn_cast()) - return Ctor->getArg(i); - return CallE.get()->getArg(i); - } - - SourceRange getArgSourceRange(unsigned i) const { - assert(i < getNumArgs()); - if (CallE) - return getArg(i)->getSourceRange(); - return Msg.getArgSourceRange(i); - } - - SourceRange getReceiverSourceRange() const { - assert(isObjCMessage()); - return Msg.getReceiverSourceRange(); - } - - /// \brief Check if one of the arguments might be a callback. - bool hasNonZeroCallbackArg() const; - - - /// \brief Check if the name corresponds to a CoreFoundation or CoreGraphics - /// function that allows objects to escape. - /// - /// Many methods allow a tracked object to escape. For example: - /// - /// CFMutableDictionaryRef x = CFDictionaryCreateMutable(..., customDeallocator); - /// CFDictionaryAddValue(y, key, x); - /// - /// We handle this and similar cases with the following heuristic. If the - /// function name contains "InsertValue", "SetValue", "AddValue", - /// "AppendValue", or "SetAttribute", then we assume that arguments may - /// escape. - // - // TODO: To reduce false negatives here, we should track the container - // allocation site and check if a proper deallocator was set there. - static bool isCFCGAllowingEscape(StringRef FName); - - // Check if this kind of expression can be inlined by the analyzer. - static bool canBeInlined(const Stmt *S) { - return isa(S); - } -}; - } } diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 0e13ddcd7e..29bb9c8f25 100644 --- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -1316,8 +1316,7 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state, // functions not handled by this checker.) bool MallocChecker::doesNotFreeMemory(const CallEvent *Call, ProgramStateRef State) const { - if (!Call) - return false; + assert(Call); // For now, assume that any C++ call can free memory. // TODO: If we want to be more optimistic here, we'll need to make sure that @@ -1326,15 +1325,11 @@ bool MallocChecker::doesNotFreeMemory(const CallEvent *Call, if (!(isa(Call) || isa(Call))) return false; - // If the call has a callback as an argument, assume the memory - // can be freed. - if (Call->hasNonZeroCallbackArg()) - return false; - // Check Objective-C messages by selector name. if (const ObjCMessageInvocation *Msg = dyn_cast(Call)){ - // If it's not a framework call, assume it frees memory. - if (!Call->isInSystemHeader()) + // If it's not a framework call, or if it takes a callback, assume it + // can free memory. + if (!Call->isInSystemHeader() || Call->hasNonZeroCallbackArg()) return false; Selector S = Msg->getSelector(); @@ -1395,13 +1390,8 @@ bool MallocChecker::doesNotFreeMemory(const CallEvent *Call, return false; StringRef FName = II->getName(); - // White list thread local storage. - if (FName.equals("pthread_setspecific")) - return false; - if (FName.equals("xpc_connection_set_context")) - return false; - // White list the 'XXXNoCopy' CoreFoundation functions. + // We specifically check these before if (FName.endswith("NoCopy")) { // Look for the deallocator argument. We know that the memory ownership // is not transferred only if the deallocator argument is @@ -1417,18 +1407,13 @@ bool MallocChecker::doesNotFreeMemory(const CallEvent *Call, return false; } - // PR12101 - // Many CoreFoundation and CoreGraphics might allow a tracked object - // to escape. - if (CallOrObjCMessage::isCFCGAllowingEscape(FName)) - return false; - // Associating streams with malloced buffers. The pointer can escape if - // 'closefn' is specified (and if that function does free memory). + // 'closefn' is specified (and if that function does free memory), + // but it will not if closefn is not specified. // Currently, we do not inspect the 'closefn' function (PR12101). if (FName == "funopen") - if (Call->getNumArgs() >= 4 && !Call->getArgSVal(4).isConstant(0)) - return false; + if (Call->getNumArgs() >= 4 && Call->getArgSVal(4).isConstant(0)) + return true; // Do not warn on pointers passed to 'setbuf' when used with std streams, // these leaks might be intentional when setting the buffer for stdio. @@ -1457,9 +1442,11 @@ bool MallocChecker::doesNotFreeMemory(const CallEvent *Call, return false; } - // Whitelist NSXXInsertXX, for example NSMapInsertIfAbsent, since they can - // be deallocated by NSMapRemove. - if (FName.startswith("NS") && (FName.find("Insert") != StringRef::npos)) + // Handle cases where we know a buffer's /address/ can escape. + // Note that the above checks handle some special cases where we know that + // even though the address escapes, it's still our responsibility to free the + // buffer. + if (Call->argumentsMayEscape()) return false; // Otherwise, assume that the function does not free memory. diff --git a/lib/StaticAnalyzer/Core/CMakeLists.txt b/lib/StaticAnalyzer/Core/CMakeLists.txt index 0061ce1045..4506d2791a 100644 --- a/lib/StaticAnalyzer/Core/CMakeLists.txt +++ b/lib/StaticAnalyzer/Core/CMakeLists.txt @@ -25,7 +25,6 @@ add_clang_library(clangStaticAnalyzerCore FunctionSummary.cpp HTMLDiagnostics.cpp MemRegion.cpp - ObjCMessage.cpp PathDiagnostic.cpp PlistDiagnostics.cpp ProgramState.cpp diff --git a/lib/StaticAnalyzer/Core/Calls.cpp b/lib/StaticAnalyzer/Core/Calls.cpp index ea28cfdf36..510f8340a2 100644 --- a/lib/StaticAnalyzer/Core/Calls.cpp +++ b/lib/StaticAnalyzer/Core/Calls.cpp @@ -118,51 +118,12 @@ static bool isPointerToConst(QualType Ty) { // Try to retrieve the function declaration and find the function parameter // types which are pointers/references to a non-pointer const. -// We do not invalidate the corresponding argument regions. +// We will not invalidate the corresponding argument regions. static void findPtrToConstParams(llvm::SmallSet &PreserveArgs, const CallEvent &Call) { - const Decl *CallDecl = Call.getDecl(); - if (!CallDecl) - return; - - if (Call.hasNonZeroCallbackArg()) - return; - - if (const FunctionDecl *FDecl = dyn_cast(CallDecl)) { - const IdentifierInfo *II = FDecl->getIdentifier(); - - // List the cases, where the region should be invalidated even if the - // argument is const. - // FIXME: This is conflating invalidating /contents/ and invalidating - // /metadata/. Now that we pass the CallEvent to the checkers, they - // should probably be doing this work themselves. - if (II) { - StringRef FName = II->getName(); - // - 'int pthread_setspecific(ptheread_key k, const void *)' stores a - // value into thread local storage. The value can later be retrieved with - // 'void *ptheread_getspecific(pthread_key)'. So even thought the - // parameter is 'const void *', the region escapes through the call. - // - funopen - sets a buffer for future IO calls. - // - ObjC functions that end with "NoCopy" can free memory, of the passed - // in buffer. - // - Many CF containers allow objects to escape through custom - // allocators/deallocators upon container construction. - // - NSXXInsertXX, for example NSMapInsertIfAbsent, since they can - // be deallocated by NSMapRemove. - // - Any call that has a callback as one of the arguments. - if (FName == "pthread_setspecific" || - FName == "funopen" || - FName.endswith("NoCopy") || - (FName.startswith("NS") && - (FName.find("Insert") != StringRef::npos)) || - CallOrObjCMessage::isCFCGAllowingEscape(FName)) - return; - } - } - unsigned Idx = 0; for (CallEvent::param_type_iterator I = Call.param_type_begin(), - E = Call.param_type_end(); + E = Call.param_type_end(); I != E; ++I, ++Idx) { if (isPointerToConst(*I)) PreserveArgs.insert(Idx); @@ -178,7 +139,8 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount, // Indexes of arguments whose values will be preserved by the call. llvm::SmallSet PreserveArgs; - findPtrToConstParams(PreserveArgs, *this); + if (!argumentsMayEscape()) + findPtrToConstParams(PreserveArgs, *this); for (unsigned Idx = 0, Count = getNumArgs(); Idx != Count; ++Idx) { if (PreserveArgs.count(Idx)) @@ -233,6 +195,11 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount, BlockCount, LCtx, /*Symbols=*/0, this); } +bool CallEvent::mayBeInlined(const Stmt *S) { + return isa(S); +} + + CallEvent::param_iterator AnyFunctionCall::param_begin() const { const FunctionDecl *D = getDecl(); if (!D) @@ -257,6 +224,63 @@ QualType AnyFunctionCall::getDeclaredResultType() const { return D->getResultType(); } +bool AnyFunctionCall::argumentsMayEscape() const { + if (CallEvent::argumentsMayEscape()) + return true; + + const FunctionDecl *D = getDecl(); + if (!D) + return true; + + const IdentifierInfo *II = D->getIdentifier(); + if (!II) + return true; + + // This set of "escaping" APIs is + + // - 'int pthread_setspecific(ptheread_key k, const void *)' stores a + // value into thread local storage. The value can later be retrieved with + // 'void *ptheread_getspecific(pthread_key)'. So even thought the + // parameter is 'const void *', the region escapes through the call. + if (II->isStr("pthread_setspecific")) + return true; + + // - xpc_connection_set_context stores a value which can be retrieved later + // with xpc_connection_get_context. + if (II->isStr("xpc_connection_set_context")) + return true; + + // - funopen - sets a buffer for future IO calls. + if (II->isStr("funopen")) + return true; + + StringRef FName = II->getName(); + + // - CoreFoundation functions that end with "NoCopy" can free a passed-in + // buffer even if it is const. + if (FName.endswith("NoCopy")) + return true; + + // - NSXXInsertXX, for example NSMapInsertIfAbsent, since they can + // be deallocated by NSMapRemove. + if (FName.startswith("NS") && (FName.find("Insert") != StringRef::npos)) + return true; + + // - Many CF containers allow objects to escape through custom + // allocators/deallocators upon container construction. (PR12101) + if (FName.startswith("CF") || FName.startswith("CG")) { + return StrInStrNoCase(FName, "InsertValue") != StringRef::npos || + StrInStrNoCase(FName, "AddValue") != StringRef::npos || + StrInStrNoCase(FName, "SetValue") != StringRef::npos || + StrInStrNoCase(FName, "WithData") != StringRef::npos || + StrInStrNoCase(FName, "AppendValue") != StringRef::npos || + StrInStrNoCase(FName, "SetAttribute") != StringRef::npos; + } + + return false; +} + + const FunctionDecl *SimpleCall::getDecl() const { const FunctionDecl *D = CE->getDirectCallee(); if (D) @@ -265,6 +289,7 @@ const FunctionDecl *SimpleCall::getDecl() const { return getSVal(CE->getCallee()).getAsFunctionDecl(); } + void CXXMemberCall::addExtraInvalidatedRegions(RegionList &Regions) const { const Expr *Base = getOriginExpr()->getImplicitObjectArgument(); @@ -277,6 +302,7 @@ void CXXMemberCall::addExtraInvalidatedRegions(RegionList &Regions) const { Regions.push_back(R); } + const BlockDataRegion *BlockCall::getBlockRegion() const { const Expr *Callee = getOriginExpr()->getCallee(); const MemRegion *DataReg = getSVal(Callee).getAsRegion(); @@ -301,11 +327,13 @@ QualType BlockCall::getDeclaredResultType() const { return cast(BlockTy->getPointeeType())->getResultType(); } + void CXXConstructorCall::addExtraInvalidatedRegions(RegionList &Regions) const { if (Target) Regions.push_back(Target); } + CallEvent::param_iterator ObjCMessageInvocation::param_begin() const { const ObjCMethodDecl *D = getDecl(); if (!D) diff --git a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp index 52f6ba4159..aeb47a1316 100644 --- a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp +++ b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp @@ -13,7 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Calls.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/AST/Stmt.h" #include "clang/AST/ParentMap.h" @@ -69,7 +69,7 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { // (6) The 'GDM' is the same as the predecessor. // (7) The LocationContext is the same as the predecessor. // (8) The PostStmt is for a non-consumed Stmt or Expr. - // (9) The successor is a CallExpr StmtPoint (so that we would be able to + // (9) The successor is not a CallExpr StmtPoint (so that we would be able to // find it when retrying a call with no inlining). // Conditions 1 and 2. @@ -116,7 +116,7 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { // Condition 9. const ProgramPoint SuccLoc = succ->getLocation(); if (const StmtPoint *SP = dyn_cast(&SuccLoc)) - if (CallOrObjCMessage::canBeInlined(SP->getStmt())) + if (CallEvent::mayBeInlined(SP->getStmt())) return false; return true; diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 74c65c8be0..255d870186 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -18,6 +18,7 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Calls.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/AST/CharUnits.h" @@ -241,7 +242,7 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, return true; // Run before processing a call. - if (CallOrObjCMessage::canBeInlined(S.getStmt())) + if (CallEvent::mayBeInlined(S.getStmt())) return true; // Is this an expression that is consumed by another expression? If so, diff --git a/lib/StaticAnalyzer/Core/ObjCMessage.cpp b/lib/StaticAnalyzer/Core/ObjCMessage.cpp deleted file mode 100644 index a00eece506..0000000000 --- a/lib/StaticAnalyzer/Core/ObjCMessage.cpp +++ /dev/null @@ -1,175 +0,0 @@ -//===- ObjCMessage.cpp - Wrapper for ObjC messages and dot syntax -*- C++ -*--// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines ObjCMessage which serves as a common wrapper for ObjC -// message expressions or implicit messages for loading/storing ObjC properties. -// -//===----------------------------------------------------------------------===// - -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" -#include "clang/AST/DeclCXX.h" - -using namespace clang; -using namespace ento; - -QualType CallOrObjCMessage::getResultType(ASTContext &ctx) const { - QualType resultTy; - bool isLVal = false; - - if (isObjCMessage()) { - resultTy = Msg.getResultType(ctx); - } else if (const CXXConstructExpr *Ctor = - CallE.dyn_cast()) { - resultTy = Ctor->getType(); - } else { - const CallExpr *FunctionCall = CallE.get(); - - isLVal = FunctionCall->isGLValue(); - const Expr *Callee = FunctionCall->getCallee(); - if (const FunctionDecl *FD = State->getSVal(Callee, LCtx).getAsFunctionDecl()) - resultTy = FD->getResultType(); - else - resultTy = FunctionCall->getType(); - } - - if (isLVal) - resultTy = ctx.getPointerType(resultTy); - - return resultTy; -} - -SVal CallOrObjCMessage::getFunctionCallee() const { - assert(isFunctionCall()); - assert(!isCXXCall()); - const Expr *Fun = CallE.get()->getCallee()->IgnoreParens(); - return State->getSVal(Fun, LCtx); -} - -SVal CallOrObjCMessage::getCXXCallee() const { - assert(isCXXCall()); - const CallExpr *ActualCall = CallE.get(); - const Expr *callee = - cast(ActualCall)->getImplicitObjectArgument(); - - // FIXME: Will eventually need to cope with member pointers. This is - // a limitation in getImplicitObjectArgument(). - if (!callee) - return UnknownVal(); - - return State->getSVal(callee, LCtx); -} - -SVal -CallOrObjCMessage::getInstanceMessageReceiver(const LocationContext *LC) const { - assert(isObjCMessage()); - return Msg.getInstanceReceiverSVal(State, LC); -} - -const Decl *CallOrObjCMessage::getDecl() const { - if (isCXXCall()) { - const CXXMemberCallExpr *CE = - cast(CallE.dyn_cast()); - assert(CE); - return CE->getMethodDecl(); - } else if (isObjCMessage()) { - return Msg.getMethodDecl(); - } else if (isFunctionCall()) { - // In case of a C style call, use the path sensitive information to find - // the function declaration. - SVal CalleeVal = getFunctionCallee(); - return CalleeVal.getAsFunctionDecl(); - } - return 0; -} - -bool CallOrObjCMessage::isCallbackArg(unsigned Idx, const Type *T) const { - // If the parameter is 0, it's harmless. - if (getArgSVal(Idx).isZeroConstant()) - return false; - - // If a parameter is a block or a callback, assume it can modify pointer. - if (T->isBlockPointerType() || - T->isFunctionPointerType() || - T->isObjCSelType()) - return true; - - // Check if a callback is passed inside a struct (for both, struct passed by - // reference and by value). Dig just one level into the struct for now. - if (const PointerType *PT = dyn_cast(T)) - T = PT->getPointeeType().getTypePtr(); - - if (const RecordType *RT = T->getAsStructureType()) { - const RecordDecl *RD = RT->getDecl(); - for (RecordDecl::field_iterator I = RD->field_begin(), - E = RD->field_end(); I != E; ++I ) { - const Type *FieldT = I->getType().getTypePtr(); - if (FieldT->isBlockPointerType() || FieldT->isFunctionPointerType()) - return true; - } - } - return false; -} - -bool CallOrObjCMessage::hasNonZeroCallbackArg() const { - unsigned NumOfArgs = getNumArgs(); - - // Process ObjC message first. - if (!CallE) { - const ObjCMethodDecl *D = Msg.getMethodDecl(); - unsigned Idx = 0; - for (ObjCMethodDecl::param_const_iterator I = D->param_begin(), - E = D->param_end(); I != E; ++I, ++Idx) { - if (NumOfArgs <= Idx) - break; - - if (isCallbackArg(Idx, (*I)->getType().getTypePtr())) - return true; - } - return false; - } - - // Else, assume we are dealing with a Function call. - const FunctionDecl *FD = 0; - if (const CXXConstructExpr *Ctor = - CallE.dyn_cast()) - FD = Ctor->getConstructor(); - - const CallExpr * CE = CallE.get(); - FD = dyn_cast_or_null(CE->getCalleeDecl()); - - // If calling using a function pointer, assume the function does not - // have a callback. TODO: We could check the types of the arguments here. - if (!FD) - return false; - - unsigned Idx = 0; - for (FunctionDecl::param_const_iterator I = FD->param_begin(), - E = FD->param_end(); I != E; ++I, ++Idx) { - if (NumOfArgs <= Idx) - break; - - if (isCallbackArg(Idx, (*I)->getType().getTypePtr())) - return true; - } - return false; -} - -bool CallOrObjCMessage::isCFCGAllowingEscape(StringRef FName) { - if (!FName.startswith("CF") && !FName.startswith("CG")) - return false; - - return StrInStrNoCase(FName, "InsertValue") != StringRef::npos || - StrInStrNoCase(FName, "AddValue") != StringRef::npos || - StrInStrNoCase(FName, "SetValue") != StringRef::npos || - StrInStrNoCase(FName, "WithData") != StringRef::npos || - StrInStrNoCase(FName, "AppendValue") != StringRef::npos || - StrInStrNoCase(FName, "SetAttribute") != StringRef::npos; -} - - diff --git a/test/Analysis/malloc.c b/test/Analysis/malloc.c index 9596751a0d..377642cc58 100644 --- a/test/Analysis/malloc.c +++ b/test/Analysis/malloc.c @@ -717,6 +717,18 @@ FILE *useFunOpenNoReleaseFunction() { return f; // expected-warning{{leak}} } +static int readNothing(void *_ctx, char *buf, int size) { + return 0; +} +FILE *useFunOpenReadNoRelease() { + void *ctx = malloc(sizeof(int)); + FILE *f = funopen(ctx, readNothing, 0, 0, 0); + if (f == 0) { + free(ctx); + } + return f; // expected-warning{{leak}} +} + // Test setbuf, setvbuf. int my_main_no_warning() { char *p = malloc(100);