зеркало из https://github.com/microsoft/clang-1.git
[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:
Родитель
8f7bfb40b7
Коммит
2de19edab6
|
@ -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 'p' is non-null</string>
|
||||
// CHECK-NEXT: <key>message</key>
|
||||
// CHECK-NEXT: <string>Assuming 'p' 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
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче