[analyzer] Adds cplusplus.NewDelete checker that check for memory leaks, double free, and use-after-free problems of memory managed by new/delete.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@177849 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Anton Yartsev 2013-03-25 01:35:45 +00:00
Родитель 8f7bfb40b7
Коммит 2de19edab6
9 изменённых файлов: 705 добавлений и 36 удалений

Просмотреть файл

@ -1911,6 +1911,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back("-analyzer-checker=deadcode");
if (types::isCXX(Inputs[0].getType()))
CmdArgs.push_back("-analyzer-checker=cplusplus");
// Enable the following experimental checkers for testing.
CmdArgs.push_back("-analyzer-checker=security.insecureAPI.UncheckedReturn");
CmdArgs.push_back("-analyzer-checker=security.insecureAPI.getpw");

Просмотреть файл

@ -166,6 +166,14 @@ def ReturnUndefChecker : Checker<"UndefReturn">,
// C++ checkers.
//===----------------------------------------------------------------------===//
let ParentPackage = Cplusplus in {
def NewDeleteChecker : Checker<"NewDelete">,
HelpText<"Check for memory leaks, double free, and use-after-free problems. Traces memory managed by new/delete.">,
DescFile<"MallocChecker.cpp">;
} // end: "cplusplus"
let ParentPackage = CplusplusAlpha in {
def VirtualCallChecker : Checker<"VirtualCall">,
@ -276,7 +284,7 @@ def UnixAPIChecker : Checker<"API">,
DescFile<"UnixAPIChecker.cpp">;
def MallocPessimistic : Checker<"Malloc">,
HelpText<"Check for memory leaks, double free, and use-after-free problems.">,
HelpText<"Check for memory leaks, double free, and use-after-free problems. Traces memory managed by malloc()/free().">,
DescFile<"MallocChecker.cpp">;
def MallocSizeofChecker : Checker<"MallocSizeof">,
@ -292,7 +300,7 @@ def ChrootChecker : Checker<"Chroot">,
DescFile<"ChrootChecker.cpp">;
def MallocOptimistic : Checker<"MallocWithAnnotations">,
HelpText<"Check for memory leaks, double free, and use-after-free problems. Assumes that all user-defined functions which might free a pointer are annotated.">,
HelpText<"Check for memory leaks, double free, and use-after-free problems. Traces memory managed by malloc()/free(). Assumes that all user-defined functions which might free a pointer are annotated.">,
DescFile<"MallocChecker.cpp">;
def PthreadLockChecker : Checker<"PthreadLock">,

Просмотреть файл

@ -120,6 +120,8 @@ class MallocChecker : public Checker<check::DeadSymbols,
check::PreStmt<ReturnStmt>,
check::PreStmt<CallExpr>,
check::PostStmt<CallExpr>,
check::PostStmt<CXXNewExpr>,
check::PreStmt<CXXDeleteExpr>,
check::PostStmt<BlockExpr>,
check::PostObjCMessage,
check::Location,
@ -142,12 +144,15 @@ public:
struct ChecksFilter {
DefaultBool CMallocPessimistic;
DefaultBool CMallocOptimistic;
DefaultBool CNewDeleteChecker;
};
ChecksFilter Filter;
void checkPreStmt(const CallExpr *S, CheckerContext &C) const;
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const;
void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
void checkPostObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const;
void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
@ -174,6 +179,7 @@ private:
bool isMemFunction(const FunctionDecl *FD, ASTContext &C) const;
bool isFreeFunction(const FunctionDecl *FD, ASTContext &C) const;
bool isAllocationFunction(const FunctionDecl *FD, ASTContext &C) const;
bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const;
///@}
static ProgramStateRef MallocMemReturnsAttr(CheckerContext &C,
const CallExpr *CE,
@ -192,7 +198,7 @@ private:
/// Update the RefState to reflect the new memory allocation.
static ProgramStateRef MallocUpdateRefState(CheckerContext &C,
const CallExpr *CE,
const Expr *E,
ProgramStateRef state);
ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE,
@ -216,8 +222,7 @@ private:
///\brief Check if the memory associated with this symbol was released.
bool isReleased(SymbolRef Sym, CheckerContext &C) const;
bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C,
const Stmt *S = 0) const;
bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const;
/// Check if the function is known not to free memory, or if it is
/// "interesting" and should be modeled explicitly.
@ -281,14 +286,14 @@ private:
inline bool isAllocated(const RefState *S, const RefState *SPrev,
const Stmt *Stmt) {
// Did not track -> allocated. Other state (released) -> allocated.
return (Stmt && isa<CallExpr>(Stmt) &&
return (Stmt && (isa<CallExpr>(Stmt) || isa<CXXNewExpr>(Stmt)) &&
(S && S->isAllocated()) && (!SPrev || !SPrev->isAllocated()));
}
inline bool isReleased(const RefState *S, const RefState *SPrev,
const Stmt *Stmt) {
// Did not track -> released. Other state (allocated) -> released.
return (Stmt && isa<CallExpr>(Stmt) &&
return (Stmt && (isa<CallExpr>(Stmt) || isa<CXXDeleteExpr>(Stmt)) &&
(S && S->isReleased()) && (!SPrev || !SPrev->isReleased()));
}
@ -398,6 +403,9 @@ bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const {
if (isAllocationFunction(FD, C))
return true;
if (isStandardNewDelete(FD, C))
return true;
return false;
}
@ -449,6 +457,36 @@ bool MallocChecker::isFreeFunction(const FunctionDecl *FD, ASTContext &C) const
return false;
}
bool MallocChecker::isStandardNewDelete(const FunctionDecl *FD,
ASTContext &C) const {
if (!FD)
return false;
OverloadedOperatorKind Kind = FD->getOverloadedOperator();
if (Kind != OO_New && Kind != OO_Array_New &&
Kind != OO_Delete && Kind != OO_Array_Delete)
return false;
// Skip custom new operators.
if (!FD->isImplicit() &&
!C.getSourceManager().isInSystemHeader(FD->getLocStart()))
return false;
// Return true if tested operator is a standard placement nothrow operator.
if (FD->getNumParams() == 2) {
QualType T = FD->getParamDecl(1)->getType();
if (const IdentifierInfo *II = T.getBaseTypeIdentifier())
return II->getName().equals("nothrow_t");
}
// Skip placement operators.
if (FD->getNumParams() != 1 || FD->isVariadic())
return false;
// One of the standard new/new[]/delete/delete[] non-placement operators.
return true;
}
void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
if (C.wasInlined)
return;
@ -464,22 +502,42 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
initIdentifierInfo(C.getASTContext());
IdentifierInfo *FunI = FD->getIdentifier();
if (FunI == II_malloc || FunI == II_valloc) {
if (CE->getNumArgs() < 1)
return;
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
} else if (FunI == II_realloc) {
State = ReallocMem(C, CE, false);
} else if (FunI == II_reallocf) {
State = ReallocMem(C, CE, true);
} else if (FunI == II_calloc) {
State = CallocMem(C, CE);
} else if (FunI == II_free) {
State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory);
} else if (FunI == II_strdup) {
State = MallocUpdateRefState(C, CE, State);
} else if (FunI == II_strndup) {
State = MallocUpdateRefState(C, CE, State);
if (Filter.CMallocOptimistic || Filter.CMallocPessimistic) {
if (FunI == II_malloc || FunI == II_valloc) {
if (CE->getNumArgs() < 1)
return;
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
} else if (FunI == II_realloc) {
State = ReallocMem(C, CE, false);
} else if (FunI == II_reallocf) {
State = ReallocMem(C, CE, true);
} else if (FunI == II_calloc) {
State = CallocMem(C, CE);
} else if (FunI == II_free) {
State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory);
} else if (FunI == II_strdup) {
State = MallocUpdateRefState(C, CE, State);
} else if (FunI == II_strndup) {
State = MallocUpdateRefState(C, CE, State);
}
}
if (Filter.CNewDeleteChecker) {
if (isStandardNewDelete(FD, C.getASTContext())) {
// Process direct calls to operator new/new[]/delete/delete[] functions
// as distinct from new/new[]/delete/delete[] expressions that are
// processed by the checkPostStmt callbacks for CXXNewExpr and
// CXXDeleteExpr.
OverloadedOperatorKind K = FD->getOverloadedOperator();
if (K == OO_New)
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
else if (K == OO_Array_New)
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
else if (K == OO_Delete || K == OO_Array_Delete)
State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory);
else
llvm_unreachable("not a new/delete operator");
}
}
}
@ -505,6 +563,51 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
C.addTransition(State);
}
void MallocChecker::checkPostStmt(const CXXNewExpr *NE,
CheckerContext &C) const {
if (NE->getNumPlacementArgs())
for (CXXNewExpr::const_arg_iterator I = NE->placement_arg_begin(),
E = NE->placement_arg_end(); I != E; ++I)
if (SymbolRef Sym = C.getSVal(*I).getAsSymbol())
checkUseAfterFree(Sym, C, *I);
if (!Filter.CNewDeleteChecker)
return;
if (!isStandardNewDelete(NE->getOperatorNew(), C.getASTContext()))
return;
ProgramStateRef State = C.getState();
// The return value from operator new is bound to a specified initialization
// value (if any) and we don't want to loose this value. So we call
// MallocUpdateRefState() instead of MallocMemAux() which breakes the
// existing binding.
State = MallocUpdateRefState(C, NE, State);
C.addTransition(State);
}
void MallocChecker::checkPreStmt(const CXXDeleteExpr *DE,
CheckerContext &C) const {
if (!Filter.CNewDeleteChecker) {
if (SymbolRef Sym = C.getSVal(DE->getArgument()).getAsSymbol())
checkUseAfterFree(Sym, C, DE->getArgument());
return;
}
if (!isStandardNewDelete(DE->getOperatorDelete(), C.getASTContext()))
return;
ProgramStateRef State = C.getState();
bool ReleasedAllocated;
State = FreeMemAux(C, DE->getArgument(), DE, State,
/*Hold*/false, ReleasedAllocated);
C.addTransition(State);
}
static bool isKnownDeallocObjCMethodName(const ObjCMethodCall &Call) {
// If the first selector piece is one of the names below, assume that the
// object takes ownership of the memory, promising to eventually deallocate it
@ -607,10 +710,10 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
}
ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C,
const CallExpr *CE,
const Expr *E,
ProgramStateRef state) {
// Get the return value.
SVal retVal = state->getSVal(CE, C.getLocationContext());
SVal retVal = state->getSVal(E, C.getLocationContext());
// We expect the malloc functions to return a pointer.
if (!retVal.getAs<Loc>())
@ -620,7 +723,7 @@ ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C,
assert(Sym);
// Set the symbol's state to Allocated.
return state->set<RegionState>(Sym, RefState::getAllocated(CE));
return state->set<RegionState>(Sym, RefState::getAllocated(E));
}
@ -1244,7 +1347,12 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
void MallocChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const {
// We will check for double free in the post visit.
if (isFreeFunction(C.getCalleeDecl(CE), C.getASTContext()))
if ((Filter.CMallocOptimistic || Filter.CMallocPessimistic) &&
isFreeFunction(C.getCalleeDecl(CE), C.getASTContext()))
return;
if (Filter.CNewDeleteChecker &&
isStandardNewDelete(C.getCalleeDecl(CE), C.getASTContext()))
return;
// Check use after free, when a freed pointer is passed to a call.
@ -1684,3 +1792,4 @@ void ento::register##name(CheckerManager &mgr) {\
REGISTER_CHECKER(MallocPessimistic)
REGISTER_CHECKER(MallocOptimistic)
REGISTER_CHECKER(NewDeleteChecker)

Просмотреть файл

@ -59,4 +59,28 @@ namespace std {
return 0;
}
};
class bad_alloc : public exception {
public:
bad_alloc() throw();
bad_alloc(const bad_alloc&) throw();
bad_alloc& operator=(const bad_alloc&) throw();
virtual const char* what() const throw() {
return 0;
}
};
struct nothrow_t {};
extern const nothrow_t nothrow;
}
void* operator new(std::size_t, const std::nothrow_t&) throw();
void* operator new[](std::size_t, const std::nothrow_t&) throw();
void operator delete(void*, const std::nothrow_t&) throw();
void operator delete[](void*, const std::nothrow_t&) throw();
void* operator new (std::size_t size, void* ptr) throw() { return ptr; };
void* operator new[] (std::size_t size, void* ptr) throw() { return ptr; };
void operator delete (void* ptr, void*) throw() {};
void operator delete[] (void* ptr, void*) throw() {};

Просмотреть файл

@ -0,0 +1,156 @@
// RUN: %clang_cc1 -analyze -analyzer-checker=core,cplusplus.NewDelete -analyzer-store region -std=c++11 -fblocks -verify %s
#include "Inputs/system-header-simulator-cxx.h"
#include "Inputs/system-header-simulator-objc.h"
typedef __typeof__(sizeof(int)) size_t;
extern "C" void *malloc(size_t);
extern "C" void free(void *);
int *global;
//------------------
// check for leaks
//------------------
void testGlobalExprNewBeforeOverload1() {
int *p = new int;
} // expected-warning{{Memory is never released; potential leak}}
void testGlobalExprNewBeforeOverload2() {
int *p = ::new int;
} // expected-warning{{Memory is never released; potential leak}}
void testGlobalOpNewBeforeOverload() {
void *p = operator new(0);
} // expected-warning{{Memory is never released; potential leak}}
void testMemIsOnHeap() {
int *p = new int;
if (global != p)
global = p;
} // expected-warning{{Memory is never released; potential leak}}
//FIXME: currently a memory region for 'new' is not a heap region, that lead to
//false-positive 'memory leak' ('global != p' is not evaluated to true and 'p'
//does not escape)
void *operator new(std::size_t);
void *operator new(std::size_t, double d);
void *operator new[](std::size_t);
void *operator new[](std::size_t, double d);
void testExprPlacementNew() {
int i;
int *p1 = new(&i) int; // no warn - standard placement new
int *p2 = new(1.0) int; // no warn - overloaded placement new
int *p3 = new (std::nothrow) int;
} // expected-warning{{Memory is never released; potential leak}}
void testExprPlacementNewArray() {
int i;
int *p1 = new(&i) int[1]; // no warn - standard placement new[]
int *p2 = new(1.0) int[1]; // no warn - overloaded placement new[]
int *p3 = new (std::nothrow) int[1];
} // expected-warning{{Memory is never released; potential leak}}
void testCustomOpNew() {
void *p = operator new(0); // no warn - call to a custom new
}
void testGlobalExprNew() {
void *p = ::new int; // no warn - call to a custom new
}
void testCustomExprNew() {
int *p = new int; // no warn - call to a custom new
}
void testGlobalExprNewArray() {
void *p = ::new int[1]; // no warn - call to a custom new
}
void testOverloadedExprNewArray() {
int *p = new int[1]; // no warn - call to a custom new
}
//---------------
// other checks
//---------------
void f(int *);
void testUseAfterDelete() {
int *p = new int;
delete p;
f(p); // expected-warning{{Use of memory after it is freed}}
}
void testDeleteAlloca() {
int *p = (int *)__builtin_alloca(sizeof(int));
delete p; // expected-warning{{Argument to free() was allocated by alloca(), not malloc()}}
}
void testDoubleDelete() {
int *p = new int;
delete p;
delete p; // expected-warning{{Attempt to free released memory}}
}
void testExprDeleteArg() {
int i;
delete &i; // expected-warning{{Argument to free() is the address of the local variable 'i', which is not memory allocated by malloc()}}
} // FIXME: 'free()' -> 'delete'; 'malloc()' -> 'new'
void testExprDeleteArrArg() {
int i;
delete[] &i; // expected-warning{{Argument to free() is the address of the local variable 'i', which is not memory allocated by malloc()}}
} // FIXME: 'free()' -> 'delete[]'; 'malloc()' -> 'new[]'
void testAllocDeallocNames() {
int *p = new(std::nothrow) int[1];
delete[] (++p); // expected-warning{{Argument to free() is offset by 4 bytes from the start of memory allocated by malloc()}}
} // FIXME: 'free()' -> 'delete[]'; 'malloc()' -> 'new[]'
//----------------------------------------------------------------------------
// Check for intersections with unix.Malloc and unix.MallocWithAnnotations
// checkers bounded with cplusplus.NewDelete.
//----------------------------------------------------------------------------
// malloc()/free() are subjects of unix.Malloc and unix.MallocWithAnnotations
void testMallocFreeNoWarn() {
int i;
free(&i); // no-warning
int *p1 = (int *)malloc(sizeof(int));
free(++p1); // no-warning
int *p2 = (int *)malloc(sizeof(int));
free(p2);
free(p2); // no-warning
int *p3 = (int *)malloc(sizeof(int)); // no-warning
}
void testFreeNewed() {
int *p = new int;
free(p); // pointer escaped, no-warning
}
void testObjcFreeNewed() {
int *p = new int;
NSData *nsdata = [NSData dataWithBytesNoCopy:p length:sizeof(int) freeWhenDone:1]; // pointer escaped, no-warning
}
void testFreeAfterDelete() {
int *p = new int;
delete p;
free(p); // expected-warning{{Use of memory after it is freed}}
}
void testStandardPlacementNewAfterDelete() {
int *p = new int;
delete p;
p = new(p) int; // expected-warning{{Use of memory after it is freed}}
}

Просмотреть файл

@ -0,0 +1,323 @@
// RUN: %clang_cc1 -analyze -analyzer-checker=cplusplus.NewDelete -analyzer-output=text -verify %s
// RUN: %clang_cc1 -analyze -analyzer-checker=cplusplus.NewDelete -analyzer-output=plist %s -o %t.plist
// RUN: FileCheck --input-file=%t.plist %s
void test() {
int *p = new int;
// expected-note@-1 {{Memory is allocated}}
if (p)
// expected-note@-1 {{Assuming 'p' is non-null}}
// expected-note@-2 {{Taking true branch}}
delete p;
// expected-note@-1 {{Memory is released}}
delete p; // expected-warning {{Attempt to free released memory}}
// expected-note@-1 {{Attempt to free released memory}}
}
// CHECK: <key>diagnostics</key>
// CHECK-NEXT:<array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>path</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>kind</key><string>control</string>
// CHECK-NEXT: <key>edges</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>start</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>6</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>6</integer>
// CHECK-NEXT: <key>col</key><integer>5</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: <key>end</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>6</integer>
// CHECK-NEXT: <key>col</key><integer>12</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>6</integer>
// CHECK-NEXT: <key>col</key><integer>14</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>kind</key><string>event</string>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>6</integer>
// CHECK-NEXT: <key>col</key><integer>12</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>ranges</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>6</integer>
// CHECK-NEXT: <key>col</key><integer>12</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>6</integer>
// CHECK-NEXT: <key>col</key><integer>18</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </array>
// CHECK-NEXT: <key>depth</key><integer>0</integer>
// CHECK-NEXT: <key>extended_message</key>
// CHECK-NEXT: <string>Memory is allocated</string>
// CHECK-NEXT: <key>message</key>
// CHECK-NEXT: <string>Memory is allocated</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>kind</key><string>control</string>
// CHECK-NEXT: <key>edges</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>start</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>6</integer>
// CHECK-NEXT: <key>col</key><integer>12</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>6</integer>
// CHECK-NEXT: <key>col</key><integer>14</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: <key>end</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>8</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>8</integer>
// CHECK-NEXT: <key>col</key><integer>4</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>kind</key><string>control</string>
// CHECK-NEXT: <key>edges</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>start</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>8</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>8</integer>
// CHECK-NEXT: <key>col</key><integer>4</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: <key>end</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>8</integer>
// CHECK-NEXT: <key>col</key><integer>7</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>8</integer>
// CHECK-NEXT: <key>col</key><integer>7</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>kind</key><string>event</string>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>8</integer>
// CHECK-NEXT: <key>col</key><integer>7</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>ranges</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>8</integer>
// CHECK-NEXT: <key>col</key><integer>7</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>8</integer>
// CHECK-NEXT: <key>col</key><integer>7</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </array>
// CHECK-NEXT: <key>depth</key><integer>0</integer>
// CHECK-NEXT: <key>extended_message</key>
// CHECK-NEXT: <string>Assuming &apos;p&apos; is non-null</string>
// CHECK-NEXT: <key>message</key>
// CHECK-NEXT: <string>Assuming &apos;p&apos; is non-null</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>kind</key><string>control</string>
// CHECK-NEXT: <key>edges</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>start</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>8</integer>
// CHECK-NEXT: <key>col</key><integer>7</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>8</integer>
// CHECK-NEXT: <key>col</key><integer>7</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: <key>end</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>11</integer>
// CHECK-NEXT: <key>col</key><integer>5</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>11</integer>
// CHECK-NEXT: <key>col</key><integer>10</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>kind</key><string>event</string>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>11</integer>
// CHECK-NEXT: <key>col</key><integer>5</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>ranges</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>11</integer>
// CHECK-NEXT: <key>col</key><integer>5</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>11</integer>
// CHECK-NEXT: <key>col</key><integer>12</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </array>
// CHECK-NEXT: <key>depth</key><integer>0</integer>
// CHECK-NEXT: <key>extended_message</key>
// CHECK-NEXT: <string>Memory is released</string>
// CHECK-NEXT: <key>message</key>
// CHECK-NEXT: <string>Memory is released</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>kind</key><string>control</string>
// CHECK-NEXT: <key>edges</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>start</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>11</integer>
// CHECK-NEXT: <key>col</key><integer>5</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>11</integer>
// CHECK-NEXT: <key>col</key><integer>10</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: <key>end</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>14</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>14</integer>
// CHECK-NEXT: <key>col</key><integer>8</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>kind</key><string>event</string>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>14</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>ranges</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>14</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>14</integer>
// CHECK-NEXT: <key>col</key><integer>10</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </array>
// CHECK-NEXT: <key>depth</key><integer>0</integer>
// CHECK-NEXT: <key>extended_message</key>
// CHECK-NEXT: <string>Attempt to free released memory</string>
// CHECK-NEXT: <key>message</key>
// CHECK-NEXT: <string>Attempt to free released memory</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: <key>description</key><string>Attempt to free released memory</string>
// CHECK-NEXT: <key>category</key><string>Memory Error</string>
// CHECK-NEXT: <key>type</key><string>Double free</string>
// CHECK-NEXT: <key>issue_context_kind</key><string>function</string>
// CHECK-NEXT: <key>issue_context</key><string>test</string>
// CHECK-NEXT: <key>issue_hash</key><string>9</string>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>14</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </dict>
// CHECK-NEXT:</array>

Просмотреть файл

@ -288,6 +288,7 @@ namespace OperatorNew {
IntWrapper *obj = new IntWrapper(42);
// should be TRUE
clang_analyzer_eval(obj->value == 42); // expected-warning{{UNKNOWN}}
delete obj;
}
void testPlacement() {

Просмотреть файл

@ -1,9 +1,11 @@
// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-store region -std=c++11 -verify %s
#include "Inputs/system-header-simulator-cxx.h"
void clang_analyzer_eval(bool);
typedef __typeof__(sizeof(int)) size_t;
extern "C" void *malloc(size_t);
extern "C" void free(void *);
int someGlobal;
void testImplicitlyDeclaredGlobalNew() {
@ -19,13 +21,6 @@ void testImplicitlyDeclaredGlobalNew() {
clang_analyzer_eval(someGlobal == 0); // expected-warning{{TRUE}}
}
// This is the standard placement new.
inline void* operator new(size_t, void* __p) throw()
{
return __p;
}
void *testPlacementNew() {
int *x = (int *)malloc(sizeof(int));
*x = 1;
@ -73,7 +68,6 @@ void testScalarInitialization() {
clang_analyzer_eval(*n == 0); // expected-warning{{TRUE}}
}
struct PtrWrapper {
int *x;
@ -85,6 +79,55 @@ PtrWrapper *testNewInvalidation() {
return new PtrWrapper(static_cast<int *>(malloc(4)));
}
//--------------------------------------------------------------------
// Check for intersection with other checkers from MallocChecker.cpp
// bounded with unix.Malloc
//--------------------------------------------------------------------
// new/delete oparators are subjects of cplusplus.NewDelete.
void testNewDeleteNoWarn() {
int i;
delete &i; // no-warning
int *p1 = new int;
delete ++p1; // no-warning
int *p2 = new int;
delete p2;
delete p2; // no-warning
int *p3 = new int; // no-warning
}
// unix.Malloc does not know about operators new/delete.
void testDeleteMallocked() {
int *x = (int *)malloc(sizeof(int));
delete x; // FIXME: Shoud detect pointer escape and keep silent after 'delete' is modeled properly.
} // expected-warning{{Memory is never released; potential leak}}
void testDeleteOpAfterFree() {
int *p = (int *)malloc(sizeof(int));
free(p);
operator delete(p); // expected-warning{{Use of memory after it is freed}}
}
void testDeleteAfterFree() {
int *p = (int *)malloc(sizeof(int));
free(p);
delete p; // expected-warning{{Use of memory after it is freed}}
}
void testStandardPlacementNewAfterFree() {
int *p = (int *)malloc(sizeof(int));
free(p);
p = new(p) int; // expected-warning{{Use of memory after it is freed}}
}
void testCustomPlacementNewAfterFree() {
int *p = (int *)malloc(sizeof(int));
free(p);
p = new(0, p) int; // expected-warning{{Use of memory after it is freed}}
}
//--------------------------------
// Incorrectly-modelled behavior
@ -95,8 +138,10 @@ int testNoInitialization() {
// Should warn that *n is uninitialized.
if (*n) { // no-warning
delete n;
return 0;
}
delete n;
return 1;
}

Просмотреть файл

@ -169,7 +169,7 @@ SBOutputDirReferencePrefix = "Ref"
# The list of checkers used during analyzes.
# Currently, consists of all the non experimental checkers.
Checkers="alpha.unix.SimpleStream,alpha.security.taint,core,deadcode,security,unix,osx"
Checkers="alpha.unix.SimpleStream,alpha.security.taint,core,cplusplus,deadcode,security,unix,osx"
Verbose = 1