aboutsummaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
diff options
context:
space:
mode:
authorAnna Zaks <ganna@apple.com>2012-02-24 23:56:53 +0000
committerAnna Zaks <ganna@apple.com>2012-02-24 23:56:53 +0000
commit3cd89ad193834e766ce5dc24e260aa8615d0d5e1 (patch)
tree23d1ef8c44b410a3aeaa4aa0a9903d02d484f6e7 /lib/StaticAnalyzer/Checkers/MallocChecker.cpp
parented878af7914df535b32d64f555fa118413186672 (diff)
[analyzer] Malloc: reason about the ObjC messages and C++.
Assume none of the ObjC messages defined in system headers free memory, except for the ones containing 'freeWhenDone' selector. Currently, just assume that the region escapes to the messages with 'freeWhenDone' (ideally, we want to treat it as 'free()'). For now, always assume that regions escape when passed to C++ methods. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@151410 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/MallocChecker.cpp')
-rw-r--r--lib/StaticAnalyzer/Checkers/MallocChecker.cpp76
1 files changed, 56 insertions, 20 deletions
diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index d6dc97a82c..9fe34f5525 100644
--- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -180,7 +180,8 @@ 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 hasUnknownBehavior(const FunctionDecl *FD, ProgramStateRef State) const;
+ bool doesNotFreeMemory(const CallOrObjCMessage *Call,
+ ProgramStateRef State) const;
static bool SummarizeValue(raw_ostream &os, SVal V);
static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR);
@@ -1053,38 +1054,75 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state,
return state;
}
-// Check if the function is not known to us. So, for example, we could
+// Check if the function is known to us. So, for example, we could
// conservatively assume it can free/reallocate it's pointer arguments.
// (We assume that the pointers cannot escape through calls to system
// functions not handled by this checker.)
-bool MallocChecker::hasUnknownBehavior(const FunctionDecl *FD,
- ProgramStateRef State) const {
- ASTContext &ASTC = State->getStateManager().getContext();
+bool MallocChecker::doesNotFreeMemory(const CallOrObjCMessage *Call,
+ ProgramStateRef State) const {
+ if (!Call)
+ return false;
+
+ // 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
+ // regions escape to C++ containers. They seem to do that even now, but for
+ // mysterious reasons.
+ if (Call->isCXXCall())
+ return false;
- // If it's one of the allocation functions we can reason about, we model it's
- // behavior explicitly.
- if (isMemFunction(FD, ASTC)) {
+ const Decl *D = Call->getDecl();
+ if (!D)
return false;
+
+ ASTContext &ASTC = State->getStateManager().getContext();
+
+ // If it's one of the allocation functions we can reason about, we model
+ // it's behavior explicitly.
+ if (isa<FunctionDecl>(D) && isMemFunction(cast<FunctionDecl>(D), ASTC)) {
+ return true;
}
- // Most system calls, do not free the memory.
+ // If it's not a system call, assume it frees memory.
SourceManager &SM = ASTC.getSourceManager();
- if (SM.isInSystemHeader(FD->getLocation())) {
- const IdentifierInfo *II = FD->getIdentifier();
+ if (!SM.isInSystemHeader(D->getLocation()))
+ return false;
+ // Process C functions.
+ if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D)) {
// White list the system functions whose arguments escape.
+ const IdentifierInfo *II = FD->getIdentifier();
if (II) {
StringRef FName = II->getName();
if (FName.equals("pthread_setspecific"))
- return true;
+ return false;
}
// Otherwise, assume that the function does not free memory.
- return false;
+ // Most system calls, do not free the memory.
+ return true;
+
+ // 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.
+ // - Anything containing 'freeWhenDone' param set to 1.
+ // Ex: dataWithBytesNoCopy:length:freeWhenDone.
+ for (unsigned i = 1; i < S.getNumArgs(); ++i) {
+ if (S.getNameForSlot(i).equals("freeWhenDone")) {
+ if (Call->getArgSVal(i).isConstant(1))
+ return false;
+ }
+ }
+
+ // Otherwise, assume that the function does not free memory.
+ // Most system calls, do not free the memory.
+ return true;
}
// Otherwise, assume that the function can free memory.
- return true;
+ return false;
+
}
// If the symbol we are tracking is invalidated, but not explicitly (ex: the &p
@@ -1100,13 +1138,11 @@ MallocChecker::checkRegionChanges(ProgramStateRef State,
return State;
llvm::SmallPtrSet<SymbolRef, 8> WhitelistedSymbols;
- const FunctionDecl *FD = (Call ?
- dyn_cast_or_null<FunctionDecl>(Call->getDecl()) :0);
-
// If it's a call which might free or reallocate memory, we assume that all
- // regions (explicit and implicit) escaped. Otherwise, whitelist explicit
- // pointers; we still can track them.
- if (!(FD && hasUnknownBehavior(FD, State))) {
+ // regions (explicit and implicit) escaped.
+
+ // Otherwise, whitelist explicit pointers; we still can track them.
+ if (!Call || doesNotFreeMemory(Call, State)) {
for (ArrayRef<const MemRegion *>::iterator I = ExplicitRegions.begin(),
E = ExplicitRegions.end(); I != E; ++I) {
if (const SymbolicRegion *R = (*I)->StripCasts()->getAs<SymbolicRegion>())