diff options
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp')
-rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp | 76 |
1 files changed, 56 insertions, 20 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index d6dc97a82c1..9fe34f55252 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/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>()) |