зеркало из https://github.com/microsoft/clang-1.git
[analyzer] Allow pointers escape through calls containing callback args.
(Since we don't have a generic pointer escape callback, modify ExprEngineCallAndReturn as well as the malloc checker.) git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@156134 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
d964d63809
Коммит
aca0ac58d2
|
@ -164,6 +164,9 @@ class CallOrObjCMessage {
|
|||
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)
|
||||
|
@ -258,6 +261,10 @@ public:
|
|||
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.
|
||||
///
|
||||
|
@ -273,18 +280,7 @@ public:
|
|||
//
|
||||
// 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) {
|
||||
if (FName[0] == 'C' && (FName[1] == 'F' || FName[1] == 'G'))
|
||||
if (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 true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static bool isCFCGAllowingEscape(StringRef FName);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1285,6 +1285,11 @@ bool MallocChecker::doesNotFreeMemory(const CallOrObjCMessage *Call,
|
|||
if (FName.startswith("NS") && (FName.find("Insert") != StringRef::npos))
|
||||
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;
|
||||
|
@ -1312,6 +1317,11 @@ bool MallocChecker::doesNotFreeMemory(const CallOrObjCMessage *Call,
|
|||
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;
|
||||
|
|
|
@ -323,12 +323,14 @@ static void findPtrToConstParams(llvm::SmallSet<unsigned, 1> &PreserveArgs,
|
|||
// 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)) ||
|
||||
Call.isCFCGAllowingEscape(FName))
|
||||
Call.isCFCGAllowingEscape(FName) ||
|
||||
Call.hasNonZeroCallbackArg())
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -344,6 +346,10 @@ static void findPtrToConstParams(llvm::SmallSet<unsigned, 1> &PreserveArgs,
|
|||
if (const ObjCMethodDecl *MDecl = dyn_cast<ObjCMethodDecl>(CallDecl)) {
|
||||
assert(MDecl->param_size() <= Call.getNumArgs());
|
||||
unsigned Idx = 0;
|
||||
|
||||
if (Call.hasNonZeroCallbackArg())
|
||||
return;
|
||||
|
||||
for (clang::ObjCMethodDecl::param_const_iterator
|
||||
I = MDecl->param_begin(), E = MDecl->param_end(); I != E; ++I, ++Idx) {
|
||||
if (isPointerToConst(*I))
|
||||
|
|
|
@ -88,3 +88,69 @@ const Decl *CallOrObjCMessage::getDecl() const {
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool CallOrObjCMessage::isCallbackArg(unsigned Idx, const Type *T) const {
|
||||
// Should we dig into struct fields, arrays ect?
|
||||
if (T->isBlockPointerType() || T->isFunctionPointerType())
|
||||
if (!getArgSVal(Idx).isZeroConstant())
|
||||
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<const CXXConstructExpr *>())
|
||||
FD = Ctor->getConstructor();
|
||||
|
||||
const CallExpr * CE = CallE.get<const CallExpr *>();
|
||||
FD = dyn_cast<FunctionDecl>(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[0] == 'C' && (FName[1] == 'F' || FName[1] == 'G'))
|
||||
if (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 true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -798,6 +798,27 @@ void radar_11358224_test_double_assign_ints_positive_2()
|
|||
ptr = ptr; // expected-warning {{leak}}
|
||||
}
|
||||
|
||||
// Assume that functions which take a function pointer can free memory even if
|
||||
// they are defined in system headers and take the const pointer to the
|
||||
// allocated memory. (radar://11160612)
|
||||
int const_ptr_and_callback(int, const char*, int n, void(*)(void*));
|
||||
void r11160612_1() {
|
||||
char *x = malloc(12);
|
||||
const_ptr_and_callback(0, x, 12, free); // no - warning
|
||||
}
|
||||
|
||||
// Null is passed as callback.
|
||||
void r11160612_2() {
|
||||
char *x = malloc(12);
|
||||
const_ptr_and_callback(0, x, 12, 0); // expected-warning {{leak}}
|
||||
}
|
||||
|
||||
// Callback is passed to a function defined in a system header.
|
||||
void r11160612_4() {
|
||||
char *x = malloc(12);
|
||||
sqlite3_bind_text_my(0, x, 12, free); // no - warning
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Below are the known false positives.
|
||||
|
||||
|
|
|
@ -14,3 +14,13 @@ struct Foo {
|
|||
Foo aFunction() {
|
||||
return malloc(10);
|
||||
}
|
||||
|
||||
// Assume that functions which take a function pointer can free memory even if
|
||||
// they are defined in system headers and take the const pointer to the
|
||||
// allocated memory. (radar://11160612)
|
||||
// Test default parameter.
|
||||
int const_ptr_and_callback_def_param(int, const char*, int n, void(*)(void*) = 0);
|
||||
void r11160612_3() {
|
||||
char *x = (char*)malloc(12);
|
||||
const_ptr_and_callback_def_param(0, x, 12);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc -analyzer-store=region -verify -Wno-objc-root-class %s
|
||||
// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc -analyzer-store=region -verify -Wno-objc-root-class -fblocks %s
|
||||
#include "system-header-simulator-objc.h"
|
||||
|
||||
@class NSString;
|
||||
|
|
|
@ -153,4 +153,29 @@ static void releaseDataCallback (void *info, const void *data, size_t size) {
|
|||
void testCGDataProviderCreateWithData() {
|
||||
void* b = calloc(8, 8);
|
||||
CGDataProviderRef p = CGDataProviderCreateWithData(0, b, 8*8, releaseDataCallback);
|
||||
}
|
||||
|
||||
// Assume that functions which take a function pointer can free memory even if
|
||||
// they are defined in system headers and take the const pointer to the
|
||||
// allocated memory. (radar://11160612)
|
||||
extern CGDataProviderRef UnknownFunWithCallback(void *info,
|
||||
const void *data, size_t size,
|
||||
CGDataProviderReleaseDataCallback releaseData)
|
||||
__attribute__((visibility("default")));
|
||||
void testUnknownFunWithCallBack() {
|
||||
void* b = calloc(8, 8);
|
||||
CGDataProviderRef p = UnknownFunWithCallback(0, b, 8*8, releaseDataCallback);
|
||||
}
|
||||
|
||||
// Test blocks.
|
||||
void acceptBlockParam(void *, void (^block)(void *), unsigned);
|
||||
void testCallWithBlockCallback() {
|
||||
void *l = malloc(12);
|
||||
acceptBlockParam(l, ^(void *i) { free(i); }, sizeof(char *));
|
||||
}
|
||||
|
||||
// Test blocks in system headers.
|
||||
void testCallWithBlockCallbackInSystem() {
|
||||
void *l = malloc(12);
|
||||
SystemHeaderFunctionWithBlockParam(l, ^(void *i) { free(i); }, sizeof(char *));
|
||||
}
|
|
@ -112,3 +112,5 @@ extern void CFRelease(CFTypeRef cf);
|
|||
extern CFMutableStringRef CFStringCreateMutableWithExternalCharactersNoCopy(CFAllocatorRef alloc, UniChar *chars, CFIndex numChars, CFIndex capacity, CFAllocatorRef externalCharactersAllocator);
|
||||
extern CFStringRef CFStringCreateWithCStringNoCopy(CFAllocatorRef alloc, const char *cStr, CFStringEncoding encoding, CFAllocatorRef contentsDeallocator);
|
||||
extern void CFStringAppend(CFMutableStringRef theString, CFStringRef appendedString);
|
||||
|
||||
void SystemHeaderFunctionWithBlockParam(void *, void (^block)(void *), unsigned);
|
||||
|
|
|
@ -36,3 +36,4 @@ FILE *funopen(const void *,
|
|||
fpos_t (*)(void *, fpos_t, int),
|
||||
int (*)(void *));
|
||||
|
||||
int sqlite3_bind_text_my(int, const char*, int n, void(*)(void*));
|
||||
|
|
Загрузка…
Ссылка в новой задаче