aboutsummaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
diff options
context:
space:
mode:
authorJordan Rose <jordan_rose@apple.com>2012-07-02 19:27:35 +0000
committerJordan Rose <jordan_rose@apple.com>2012-07-02 19:27:35 +0000
commit740d490593e0de8732a697c9f77b90ddd463863b (patch)
tree1c4003bd0e0d2be292345e6ac90af5628e1734c0 /lib/StaticAnalyzer/Checkers/MallocChecker.cpp
parent7518b3784ed2176aad8dcabe0685c6e02c5f1043 (diff)
[analyzer] Add a new abstraction over all types of calls: CallEvent
This is intended to replace CallOrObjCMessage, and is eventually intended to be used for anything that cares more about /what/ is being called than /how/ it's being called. For example, inlining destructors should be the same as inlining blocks, and checking __attribute__((nonnull)) should apply to the allocator calls generated by operator new. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@159554 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/MallocChecker.cpp')
-rw-r--r--lib/StaticAnalyzer/Checkers/MallocChecker.cpp248
1 files changed, 119 insertions, 129 deletions
diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 1aa9b824e3..0e13ddcd7e 100644
--- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -18,7 +18,7 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/Calls.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
@@ -140,7 +140,7 @@ public:
const StoreManager::InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call) const;
+ const CallEvent *Call) const;
bool wantsRegionChangeUpdate(ProgramStateRef state) const {
return true;
}
@@ -200,7 +200,7 @@ private:
/// Check if the function is not known to us. So, for example, we could
/// conservatively assume it can free/reallocate it's pointer arguments.
- bool doesNotFreeMemory(const CallOrObjCMessage *Call,
+ bool doesNotFreeMemory(const CallEvent *Call,
ProgramStateRef State) const;
static bool SummarizeValue(raw_ostream &os, SVal V);
@@ -481,7 +481,8 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
C.addTransition(State);
}
-static bool isFreeWhenDoneSetToZero(CallOrObjCMessage Call, Selector &S) {
+static bool isFreeWhenDoneSetToZero(const ObjCMessageInvocation &Call,
+ Selector &S) {
for (unsigned i = 1; i < S.getNumArgs(); ++i)
if (S.getNameForSlot(i).equals("freeWhenDone"))
if (Call.getArgSVal(i).isConstant(0))
@@ -491,12 +492,12 @@ static bool isFreeWhenDoneSetToZero(CallOrObjCMessage Call, Selector &S) {
}
void MallocChecker::checkPreObjCMessage(const ObjCMessage &Msg,
- CheckerContext &C) const {
+ CheckerContext &C) const {
const ObjCMethodDecl *MD = Msg.getMethodDecl();
if (!MD)
return;
- CallOrObjCMessage Call(Msg, C.getState(), C.getLocationContext());
+ ObjCMessageInvocation Call(Msg, C.getState(), C.getLocationContext());
Selector S = Msg.getSelector();
// If the first selector is dataWithBytesNoCopy, assume that the memory will
@@ -509,7 +510,7 @@ void MallocChecker::checkPreObjCMessage(const ObjCMessage &Msg,
S.getNameForSlot(0) == "initWithCharactersNoCopy") &&
!isFreeWhenDoneSetToZero(Call, S)){
unsigned int argIdx = 0;
- C.addTransition(FreeMemAux(C, Call.getArg(argIdx),
+ C.addTransition(FreeMemAux(C, Call.getArgExpr(argIdx),
Msg.getMessageExpr(), C.getState(), true));
}
}
@@ -1310,10 +1311,10 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state,
}
// Check if the function is known to us. So, for example, we could
-// conservatively assume it can free/reallocate it's pointer arguments.
+// conservatively assume it can free/reallocate its pointer arguments.
// (We assume that the pointers cannot escape through calls to system
// functions not handled by this checker.)
-bool MallocChecker::doesNotFreeMemory(const CallOrObjCMessage *Call,
+bool MallocChecker::doesNotFreeMemory(const CallEvent *Call,
ProgramStateRef State) const {
if (!Call)
return false;
@@ -1322,118 +1323,23 @@ bool MallocChecker::doesNotFreeMemory(const CallOrObjCMessage *Call,
// TODO: If we want to be more optimistic here, we'll need to make sure that
// regions escape to C++ containers. They seem to do that even now, but for
// mysterious reasons.
- if (Call->isCXXCall())
+ if (!(isa<FunctionCall>(Call) || isa<ObjCMessageInvocation>(Call)))
return false;
- const Decl *D = Call->getDecl();
- if (!D)
+ // If the call has a callback as an argument, assume the memory
+ // can be freed.
+ if (Call->hasNonZeroCallbackArg())
return false;
- ASTContext &ASTC = State->getStateManager().getContext();
-
- // If it's one of the allocation functions we can reason about, we model
- // its behavior explicitly.
- if (isa<FunctionDecl>(D) && isMemFunction(cast<FunctionDecl>(D), ASTC)) {
- return true;
- }
-
- // If it's not a system call, assume it frees memory.
- if (!Call->isInSystemHeader())
- return false;
-
- // Process C/ObjC functions.
- if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
- // White list the system functions whose arguments escape.
- const IdentifierInfo *II = FD->getIdentifier();
- if (!II)
- return true;
- StringRef FName = II->getName();
-
- // White list thread local storage.
- if (FName.equals("pthread_setspecific"))
- return false;
-
- // White list xpc connection context.
- // TODO: Ensure that the deallocation actually happens, need to reason
- // about "xpc_connection_set_finalizer_f".
- if (FName.equals("xpc_connection_set_context"))
- return false;
-
- // White list the 'XXXNoCopy' ObjC functions.
- if (FName.endswith("NoCopy")) {
- // Look for the deallocator argument. We know that the memory ownership
- // is not transferred only if the deallocator argument is
- // 'kCFAllocatorNull'.
- for (unsigned i = 1; i < Call->getNumArgs(); ++i) {
- const Expr *ArgE = Call->getArg(i)->IgnoreParenCasts();
- if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(ArgE)) {
- StringRef DeallocatorName = DE->getFoundDecl()->getName();
- if (DeallocatorName == "kCFAllocatorNull")
- return true;
- }
- }
- return false;
- }
-
- // PR12101
- // Many CoreFoundation and CoreGraphics might allow a tracked object
- // to escape.
- if (Call->isCFCGAllowingEscape(FName))
- return false;
-
- // Associating streams with malloced buffers. The pointer can escape if
- // 'closefn' is specified (and if that function does free memory).
- // Currently, we do not inspect the 'closefn' function (PR12101).
- if (FName == "funopen")
- if (Call->getNumArgs() >= 4 && !Call->getArgSVal(4).isConstant(0))
- return false;
-
- // Do not warn on pointers passed to 'setbuf' when used with std streams,
- // these leaks might be intentional when setting the buffer for stdio.
- // http://stackoverflow.com/questions/2671151/who-frees-setvbuf-buffer
- if (FName == "setbuf" || FName =="setbuffer" ||
- FName == "setlinebuf" || FName == "setvbuf") {
- if (Call->getNumArgs() >= 1)
- if (const DeclRefExpr *Arg =
- dyn_cast<DeclRefExpr>(Call->getArg(0)->IgnoreParenCasts()))
- if (const VarDecl *D = dyn_cast<VarDecl>(Arg->getDecl()))
- if (D->getCanonicalDecl()->getName().find("std")
- != StringRef::npos)
- return false;
- }
-
- // A bunch of other functions which either take ownership of a pointer or
- // wrap the result up in a struct or object, meaning it can be freed later.
- // (See RetainCountChecker.) Not all the parameters here are invalidated,
- // but the Malloc checker cannot differentiate between them. The right way
- // of doing this would be to implement a pointer escapes callback.
- if (FName == "CGBitmapContextCreate" ||
- FName == "CGBitmapContextCreateWithData" ||
- FName == "CVPixelBufferCreateWithBytes" ||
- FName == "CVPixelBufferCreateWithPlanarBytes" ||
- FName == "OSAtomicEnqueue") {
- return false;
- }
-
- // Whitelist NSXXInsertXX, for example NSMapInsertIfAbsent, since they can
- // be deallocated by NSMapRemove.
- if (FName.startswith("NS") && (FName.find("Insert") != StringRef::npos))
+ // Check Objective-C messages by selector name.
+ if (const ObjCMessageInvocation *Msg = dyn_cast<ObjCMessageInvocation>(Call)){
+ // If it's not a framework call, assume it frees memory.
+ if (!Call->isInSystemHeader())
return false;
- // If the call has a callback as an argument, assume the memory
- // can be freed.
- if (Call->hasNonZeroCallbackArg())
- return false;
-
- // Otherwise, assume that the function does not free memory.
- // Most system calls, do not free the memory.
- return true;
+ Selector S = Msg->getSelector();
- // Process ObjC functions.
- } else if (const ObjCMethodDecl * ObjCD = dyn_cast<ObjCMethodDecl>(D)) {
- Selector S = ObjCD->getSelector();
-
- // White list the ObjC functions which do free memory.
+ // Whitelist the ObjC methods which do free memory.
// - Anything containing 'freeWhenDone' param set to 1.
// Ex: dataWithBytesNoCopy:length:freeWhenDone.
for (unsigned i = 1; i < S.getNumArgs(); ++i) {
@@ -1448,33 +1354,117 @@ bool MallocChecker::doesNotFreeMemory(const CallOrObjCMessage *Call,
// If the first selector ends with NoCopy, assume that the ownership is
// transferred as well.
// Ex: [NSData dataWithBytesNoCopy:bytes length:10];
- if (S.getNameForSlot(0).endswith("NoCopy")) {
+ StringRef FirstSlot = S.getNameForSlot(0);
+ if (FirstSlot.endswith("NoCopy"))
return false;
- }
// If the first selector starts with addPointer, insertPointer,
// or replacePointer, assume we are dealing with NSPointerArray or similar.
// This is similar to C++ containers (vector); we still might want to check
- // that the pointers get freed, by following the container itself.
- if (S.getNameForSlot(0).startswith("addPointer") ||
- S.getNameForSlot(0).startswith("insertPointer") ||
- S.getNameForSlot(0).startswith("replacePointer")) {
+ // that the pointers get freed by following the container itself.
+ if (FirstSlot.startswith("addPointer") ||
+ FirstSlot.startswith("insertPointer") ||
+ FirstSlot.startswith("replacePointer")) {
return false;
}
- // If the call has a callback as an argument, assume the memory
- // can be freed.
- if (Call->hasNonZeroCallbackArg())
- return false;
+ // Otherwise, assume that the method does not free memory.
+ // Most framework methods do not free memory.
+ return true;
+ }
- // Otherwise, assume that the function does not free memory.
- // Most system calls, do not free the memory.
+ // At this point the only thing left to handle is straight function calls.
+ const FunctionDecl *FD = cast<FunctionCall>(Call)->getDecl();
+ if (!FD)
+ return false;
+
+ ASTContext &ASTC = State->getStateManager().getContext();
+
+ // If it's one of the allocation functions we can reason about, we model
+ // its behavior explicitly.
+ if (isMemFunction(FD, ASTC))
return true;
+
+ // If it's not a system call, assume it frees memory.
+ if (!Call->isInSystemHeader())
+ return false;
+
+ // White list the system functions whose arguments escape.
+ const IdentifierInfo *II = FD->getIdentifier();
+ if (!II)
+ 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.
+ if (FName.endswith("NoCopy")) {
+ // Look for the deallocator argument. We know that the memory ownership
+ // is not transferred only if the deallocator argument is
+ // 'kCFAllocatorNull'.
+ for (unsigned i = 1; i < Call->getNumArgs(); ++i) {
+ const Expr *ArgE = Call->getArgExpr(i)->IgnoreParenCasts();
+ if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(ArgE)) {
+ StringRef DeallocatorName = DE->getFoundDecl()->getName();
+ if (DeallocatorName == "kCFAllocatorNull")
+ return true;
+ }
+ }
+ return false;
}
- // Otherwise, assume that the function can free memory.
- 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).
+ // Currently, we do not inspect the 'closefn' function (PR12101).
+ if (FName == "funopen")
+ if (Call->getNumArgs() >= 4 && !Call->getArgSVal(4).isConstant(0))
+ return false;
+ // Do not warn on pointers passed to 'setbuf' when used with std streams,
+ // these leaks might be intentional when setting the buffer for stdio.
+ // http://stackoverflow.com/questions/2671151/who-frees-setvbuf-buffer
+ if (FName == "setbuf" || FName =="setbuffer" ||
+ FName == "setlinebuf" || FName == "setvbuf") {
+ if (Call->getNumArgs() >= 1) {
+ const Expr *ArgE = Call->getArgExpr(0)->IgnoreParenCasts();
+ if (const DeclRefExpr *ArgDRE = dyn_cast<DeclRefExpr>(ArgE))
+ if (const VarDecl *D = dyn_cast<VarDecl>(ArgDRE->getDecl()))
+ if (D->getCanonicalDecl()->getName().find("std") != StringRef::npos)
+ return false;
+ }
+ }
+
+ // A bunch of other functions which either take ownership of a pointer or
+ // wrap the result up in a struct or object, meaning it can be freed later.
+ // (See RetainCountChecker.) Not all the parameters here are invalidated,
+ // but the Malloc checker cannot differentiate between them. The right way
+ // of doing this would be to implement a pointer escapes callback.
+ if (FName == "CGBitmapContextCreate" ||
+ FName == "CGBitmapContextCreateWithData" ||
+ FName == "CVPixelBufferCreateWithBytes" ||
+ FName == "CVPixelBufferCreateWithPlanarBytes" ||
+ FName == "OSAtomicEnqueue") {
+ return false;
+ }
+
+ // Whitelist NSXXInsertXX, for example NSMapInsertIfAbsent, since they can
+ // be deallocated by NSMapRemove.
+ if (FName.startswith("NS") && (FName.find("Insert") != StringRef::npos))
+ return false;
+
+ // Otherwise, assume that the function does not free memory.
+ // Most system calls do not free the memory.
+ return true;
}
// If the symbol we are tracking is invalidated, but not explicitly (ex: the &p
@@ -1485,7 +1475,7 @@ MallocChecker::checkRegionChanges(ProgramStateRef State,
const StoreManager::InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call) const {
+ const CallEvent *Call) const {
if (!invalidated || invalidated->empty())
return State;
llvm::SmallPtrSet<SymbolRef, 8> WhitelistedSymbols;