bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const;
- /// Check if the function is known not to free memory, or if it is
+ /// Check if the function is known free memory, or if it is
/// "interesting" and should be modeled explicitly.
///
+ /// \param EscapingSymbol A function might not free memory in general, but
+ /// could be known to free a particular symbol. In this case, false is
+ /// returned and the single escaping symbol is returned through the out
+ /// parameter.
+ ///
/// We assume that pointers do not escape through calls to system functions
/// not handled by this checker.
- bool doesNotFreeMemOrInteresting(const CallEvent *Call,
- ProgramStateRef State) const;
+ bool mayFreeAnyEscapedMemoryOrIsModelledExplicitely(const CallEvent *Call,
+ ProgramStateRef State,
+ SymbolRef &EscapingSymbol) const;
// Implementation of the checkPointerEscape callabcks.
ProgramStateRef checkPointerEscapeAux(ProgramStateRef State,
return state;
}
-bool MallocChecker::doesNotFreeMemOrInteresting(const CallEvent *Call,
- ProgramStateRef State) const {
+bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModelledExplicitely(
+ const CallEvent *Call,
+ ProgramStateRef State,
+ SymbolRef &EscapingSymbol) const {
assert(Call);
// For now, assume that any C++ call can free memory.
// regions escape to C++ containers. They seem to do that even now, but for
// mysterious reasons.
if (!(isa<FunctionCall>(Call) || isa<ObjCMethodCall>(Call)))
- return false;
+ return true;
// Check Objective-C messages by selector name.
if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) {
// 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;
+ return true;
// If it's a method we know about, handle it explicitly post-call.
// This should happen before the "freeWhenDone" check below.
if (isKnownDeallocObjCMethodName(*Msg))
- return true;
+ return false;
// If there's a "freeWhenDone" parameter, but the method isn't one we know
// about, we can't be sure that the object will use free() to deallocate the
// memory, so we can't model it explicitly. The best we can do is use it to
// decide whether the pointer escapes.
if (Optional<bool> FreeWhenDone = getFreeWhenDoneArg(*Msg))
- return !*FreeWhenDone;
+ return *FreeWhenDone;
// If the first selector piece ends with "NoCopy", and there is no
// "freeWhenDone" parameter set to zero, we know ownership is being
// free() to deallocate the memory, so we can't model it explicitly.
StringRef FirstSlot = Msg->getSelector().getNameForSlot(0);
if (FirstSlot.endswith("NoCopy"))
- return false;
+ return true;
// If the first selector starts with addPointer, insertPointer,
// or replacePointer, assume we are dealing with NSPointerArray or similar.
if (FirstSlot.startswith("addPointer") ||
FirstSlot.startswith("insertPointer") ||
FirstSlot.startswith("replacePointer")) {
- return false;
+ return true;
}
- // We should escape on call to 'init'. This is especially relevant to the
- // receiver, as the corresponding symbol is usually not referenced after
- // the call.
- if (Msg->getMethodFamily() == OMF_init)
- return false;
+ // We should escape receiver on call to 'init'. This is especially relevant
+ // to the receiver, as the corresponding symbol is usually not referenced
+ // after the call.
+ if (Msg->getMethodFamily() == OMF_init) {
+ EscapingSymbol = Msg->getReceiverSVal().getAsSymbol();
+ return true;
+ }
// Otherwise, assume that the method does not free memory.
// Most framework methods do not free memory.
- return true;
+ return false;
}
// At this point the only thing left to handle is straight function calls.
const FunctionDecl *FD = cast<FunctionCall>(Call)->getDecl();
if (!FD)
- return false;
+ return true;
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;
+ return false;
// If it's not a system call, assume it frees memory.
if (!Call->isInSystemHeader())
- return false;
+ return true;
// White list the system functions whose arguments escape.
const IdentifierInfo *II = FD->getIdentifier();
if (!II)
- return false;
+ return true;
StringRef FName = II->getName();
// White list the 'XXXNoCopy' CoreFoundation functions.
if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(ArgE)) {
StringRef DeallocatorName = DE->getFoundDecl()->getName();
if (DeallocatorName == "kCFAllocatorNull")
- return true;
+ return false;
}
}
- return false;
+ return true;
}
// Associating streams with malloced buffers. The pointer can escape if
// Currently, we do not inspect the 'closefn' function (PR12101).
if (FName == "funopen")
if (Call->getNumArgs() >= 4 && Call->getArgSVal(4).isConstant(0))
- return true;
+ 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.
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;
+ return true;
}
}
FName == "CVPixelBufferCreateWithBytes" ||
FName == "CVPixelBufferCreateWithPlanarBytes" ||
FName == "OSAtomicEnqueue") {
- return false;
+ return true;
}
// Handle cases where we know a buffer's /address/ can escape.
// even though the address escapes, it's still our responsibility to free the
// buffer.
if (Call->argumentsMayEscape())
- return false;
+ return true;
// Otherwise, assume that the function does not free memory.
// Most system calls do not free the memory.
- return true;
+ return false;
}
static bool retTrue(const RefState *RS) {
bool(*CheckRefState)(const RefState*)) const {
// If we know that the call does not free memory, or we want to process the
// call later, keep tracking the top level arguments.
+ SymbolRef EscapingSymbol = 0;
if (Kind == PSK_DirectEscapeOnCall &&
- doesNotFreeMemOrInteresting(Call, State)) {
+ !mayFreeAnyEscapedMemoryOrIsModelledExplicitely(Call, State,
+ EscapingSymbol) &&
+ !EscapingSymbol) {
return State;
}
I != E; ++I) {
SymbolRef sym = *I;
+ if (EscapingSymbol && EscapingSymbol != sym)
+ continue;
+
if (const RefState *RS = State->get<RegionState>(sym)) {
if (RS->isAllocated() && CheckRefState(RS)) {
State = State->remove<RegionState>(sym);