}
};
+class NewAllocator {
+ template <typename CHECKER>
+ static void _checkNewAllocator(void *checker, const CXXNewExpr *NE,
+ SVal Target, CheckerContext &C) {
+ ((const CHECKER *)checker)->checkNewAllocator(NE, Target, C);
+ }
+
+public:
+ template <typename CHECKER>
+ static void _register(CHECKER *checker, CheckerManager &mgr) {
+ mgr._registerForNewAllocator(
+ CheckerManager::CheckNewAllocatorFunc(checker,
+ _checkNewAllocator<CHECKER>));
+ }
+};
+
class LiveSymbols {
template <typename CHECKER>
static void _checkLiveSymbols(void *checker, ProgramStateRef state,
ExplodedNodeSet &Dst, ExplodedNode *Pred,
ExprEngine &Eng);
+ /// \brief Run checkers between C++ operator new and constructor calls.
+ void runCheckersForNewAllocator(const CXXNewExpr *NE, SVal Target,
+ ExplodedNodeSet &Dst,
+ ExplodedNode *Pred,
+ ExprEngine &Eng,
+ bool wasInlined = false);
+
/// \brief Run checkers for live symbols.
///
/// Allows modifying SymbolReaper object. For example, checkers can explicitly
typedef CheckerFn<void (const Stmt *, CheckerContext &)>
CheckBranchConditionFunc;
+
+ typedef CheckerFn<void (const CXXNewExpr *, SVal, CheckerContext &)>
+ CheckNewAllocatorFunc;
typedef CheckerFn<void (SymbolReaper &, CheckerContext &)>
CheckDeadSymbolsFunc;
void _registerForBranchCondition(CheckBranchConditionFunc checkfn);
+ void _registerForNewAllocator(CheckNewAllocatorFunc checkfn);
+
void _registerForLiveSymbols(CheckLiveSymbolsFunc checkfn);
void _registerForDeadSymbols(CheckDeadSymbolsFunc checkfn);
std::vector<CheckBranchConditionFunc> BranchConditionCheckers;
+ std::vector<CheckNewAllocatorFunc> NewAllocatorCheckers;
+
std::vector<CheckLiveSymbolsFunc> LiveSymbolsCheckers;
std::vector<CheckDeadSymbolsFunc> DeadSymbolsCheckers;
check::PreCall,
check::PostCall,
check::BranchCondition,
+ check::NewAllocator,
check::Location,
check::Bind,
check::DeadSymbols,
/// \brief Pre-visit of the condition statement of a branch (such as IfStmt).
void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const {}
+ /// \brief Post-visit the C++ operator new's allocation call.
+ ///
+ /// Execution of C++ operator new consists of the following phases: (1) call
+ /// default or overridden operator new() to allocate memory (2) cast the
+ /// return value of operator new() from void pointer type to class pointer
+ /// type, (3) assuming that the value is non-null, call the object's
+ /// constructor over this pointer, (4) declare that the value of the
+ /// new-expression is this pointer. This callback is called between steps
+ /// (2) and (3). Post-call for the allocator is called after step (1).
+ /// Pre-statement for the new-expression is called on step (4) when the value
+ /// of the expression is evaluated.
+ /// \param NE The C++ new-expression that triggered the allocation.
+ /// \param Target The allocated region, casted to the class type.
+ void checkNewAllocator(const CXXNewExpr *NE, SVal Target,
+ CheckerContext &) const {}
+
/// \brief Called on a load from and a store to a location.
///
/// The method will be called each time a location (pointer) value is
check::PreCall,
check::PostStmt<CallExpr>,
check::PostStmt<CXXNewExpr>,
+ check::NewAllocator,
check::PreStmt<CXXDeleteExpr>,
check::PostStmt<BlockExpr>,
check::PostObjCMessage,
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const;
+ void checkNewAllocator(const CXXNewExpr *NE, SVal Target,
+ 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;
bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const;
///@}
+ /// \brief Process C++ operator new()'s allocation, which is the part of C++
+ /// new-expression that goes before the constructor.
+ void processNewAllocation(const CXXNewExpr *NE, CheckerContext &C,
+ SVal Target) const;
+
/// \brief Perform a zero-allocation check.
+ /// The optional \p RetVal parameter specifies the newly allocated pointer
+ /// value; if unspecified, the value of expression \p E is used.
ProgramStateRef ProcessZeroAllocation(CheckerContext &C, const Expr *E,
const unsigned AllocationSizeArg,
- ProgramStateRef State) const;
+ ProgramStateRef State,
+ Optional<SVal> RetVal = None) const;
ProgramStateRef MallocMemReturnsAttr(CheckerContext &C,
const CallExpr *CE,
AllocationFamily Family = AF_Malloc);
static ProgramStateRef addExtentSize(CheckerContext &C, const CXXNewExpr *NE,
- ProgramStateRef State);
+ ProgramStateRef State, SVal Target);
// Check if this malloc() for special flags. At present that means M_ZERO or
// __GFP_ZERO (in which case, treat it like calloc).
const ProgramStateRef &State) const;
/// Update the RefState to reflect the new memory allocation.
+ /// The optional \p RetVal parameter specifies the newly allocated pointer
+ /// value; if unspecified, the value of expression \p E is used.
static ProgramStateRef
MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State,
- AllocationFamily Family = AF_Malloc);
+ AllocationFamily Family = AF_Malloc,
+ Optional<SVal> RetVal = None);
ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE,
const OwnershipAttr* Att,
}
// Performs a 0-sized allocations check.
-ProgramStateRef MallocChecker::ProcessZeroAllocation(CheckerContext &C,
- const Expr *E,
- const unsigned AllocationSizeArg,
- ProgramStateRef State) const {
+ProgramStateRef MallocChecker::ProcessZeroAllocation(
+ CheckerContext &C, const Expr *E, const unsigned AllocationSizeArg,
+ ProgramStateRef State, Optional<SVal> RetVal) const {
if (!State)
return nullptr;
+ if (!RetVal)
+ RetVal = C.getSVal(E);
+
const Expr *Arg = nullptr;
if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
State->assume(SvalBuilder.evalEQ(State, *DefArgVal, Zero));
if (TrueState && !FalseState) {
- SVal retVal = C.getSVal(E);
- SymbolRef Sym = retVal.getAsLocSymbol();
+ SymbolRef Sym = RetVal->getAsLocSymbol();
if (!Sym)
return State;
return false;
}
-void MallocChecker::checkPostStmt(const CXXNewExpr *NE,
- CheckerContext &C) const {
-
+void MallocChecker::processNewAllocation(const CXXNewExpr *NE,
+ CheckerContext &C,
+ SVal Target) const {
if (NE->getNumPlacementArgs())
for (CXXNewExpr::const_arg_iterator I = NE->placement_arg_begin(),
E = NE->placement_arg_end(); I != E; ++I)
// MallocUpdateRefState() instead of MallocMemAux() which breakes the
// existing binding.
State = MallocUpdateRefState(C, NE, State, NE->isArray() ? AF_CXXNewArray
- : AF_CXXNew);
- State = addExtentSize(C, NE, State);
- State = ProcessZeroAllocation(C, NE, 0, State);
+ : AF_CXXNew, Target);
+ State = addExtentSize(C, NE, State, Target);
+ State = ProcessZeroAllocation(C, NE, 0, State, Target);
C.addTransition(State);
}
+void MallocChecker::checkPostStmt(const CXXNewExpr *NE,
+ CheckerContext &C) const {
+ if (!C.getAnalysisManager().getAnalyzerOptions().mayInlineCXXAllocator())
+ processNewAllocation(NE, C, C.getSVal(NE));
+}
+
+void MallocChecker::checkNewAllocator(const CXXNewExpr *NE, SVal Target,
+ CheckerContext &C) const {
+ if (!C.wasInlined)
+ processNewAllocation(NE, C, Target);
+}
+
// Sets the extent value of the MemRegion allocated by
// new expression NE to its size in Bytes.
//
ProgramStateRef MallocChecker::addExtentSize(CheckerContext &C,
const CXXNewExpr *NE,
- ProgramStateRef State) {
+ ProgramStateRef State,
+ SVal Target) {
if (!State)
return nullptr;
SValBuilder &svalBuilder = C.getSValBuilder();
SVal ElementCount;
- const LocationContext *LCtx = C.getLocationContext();
const SubRegion *Region;
if (NE->isArray()) {
const Expr *SizeExpr = NE->getArraySize();
ElementCount = C.getSVal(SizeExpr);
// Store the extent size for the (symbolic)region
// containing the elements.
- Region = (State->getSVal(NE, LCtx))
- .getAsRegion()
+ Region = Target.getAsRegion()
->getAs<SubRegion>()
- ->getSuperRegion()
+ ->StripCasts()
->getAs<SubRegion>();
} else {
ElementCount = svalBuilder.makeIntVal(1, true);
- Region = (State->getSVal(NE, LCtx)).getAsRegion()->getAs<SubRegion>();
+ Region = Target.getAsRegion()->getAs<SubRegion>();
}
assert(Region);
ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C,
const Expr *E,
ProgramStateRef State,
- AllocationFamily Family) {
+ AllocationFamily Family,
+ Optional<SVal> RetVal) {
if (!State)
return nullptr;
// Get the return value.
- SVal retVal = C.getSVal(E);
+ if (!RetVal)
+ RetVal = C.getSVal(E);
// We expect the malloc functions to return a pointer.
- if (!retVal.getAs<Loc>())
+ if (!RetVal->getAs<Loc>())
return nullptr;
- SymbolRef Sym = retVal.getAsLocSymbol();
+ SymbolRef Sym = RetVal->getAsLocSymbol();
+ // This is a return value of a function that was not inlined, such as malloc()
+ // or new(). We've checked that in the caller. Therefore, it must be a symbol.
assert(Sym);
// Set the symbol's state to Allocated.
expandGraphWithCheckers(C, Dst, Src);
}
+namespace {
+ struct CheckNewAllocatorContext {
+ typedef std::vector<CheckerManager::CheckNewAllocatorFunc> CheckersTy;
+ const CheckersTy &Checkers;
+ const CXXNewExpr *NE;
+ SVal Target;
+ bool WasInlined;
+ ExprEngine &Eng;
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
+
+ CheckNewAllocatorContext(const CheckersTy &Checkers, const CXXNewExpr *NE,
+ SVal Target, bool WasInlined, ExprEngine &Eng)
+ : Checkers(Checkers), NE(NE), Target(Target), WasInlined(WasInlined),
+ Eng(Eng) {}
+
+ void runChecker(CheckerManager::CheckNewAllocatorFunc checkFn,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ // TODO: Does this deserve a custom program point? For now we're re-using
+ // PostImplicitCall because we're guaranteed to use the non-implicit
+ // PostStmt for the PostCall callback, because we have some sort of
+ // call site (CXXNewExpr) in this scenario.
+ ProgramPoint L = PostImplicitCall(NE->getOperatorNew(), NE->getLocStart(),
+ Pred->getLocationContext());
+ CheckerContext C(Bldr, Eng, Pred, L, WasInlined);
+ checkFn(NE, Target, C);
+ }
+ };
+}
+
+void CheckerManager::runCheckersForNewAllocator(
+ const CXXNewExpr *NE, SVal Target, ExplodedNodeSet &Dst, ExplodedNode *Pred,
+ ExprEngine &Eng, bool WasInlined) {
+ ExplodedNodeSet Src;
+ Src.insert(Pred);
+ CheckNewAllocatorContext C(NewAllocatorCheckers, NE, Target, WasInlined, Eng);
+ expandGraphWithCheckers(C, Dst, Src);
+}
+
/// \brief Run checkers for live symbols.
void CheckerManager::runCheckersForLiveSymbols(ProgramStateRef state,
SymbolReaper &SymReaper) {
BranchConditionCheckers.push_back(checkfn);
}
+void CheckerManager::_registerForNewAllocator(CheckNewAllocatorFunc checkfn) {
+ NewAllocatorCheckers.push_back(checkfn);
+}
+
void CheckerManager::_registerForLiveSymbols(CheckLiveSymbolsFunc checkfn) {
LiveSymbolsCheckers.push_back(checkfn);
}
setCXXNewAllocatorValue(State, CNE, LCtx, State->getSVal(CNE, LCtx)));
}
- getCheckerManager().runCheckersForPostCall(Dst, DstPostValue,
- *Call, *this);
+ ExplodedNodeSet DstPostPostCallCallback;
+ getCheckerManager().runCheckersForPostCall(DstPostPostCallCallback,
+ DstPostValue, *Call, *this);
+ for (auto I : DstPostPostCallCallback) {
+ getCheckerManager().runCheckersForNewAllocator(
+ CNE, getCXXNewAllocatorValue(I->getState(), CNE, LCtx), Dst, I, *this);
+ }
}
void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
CallEventRef<> UpdatedCall = Call.cloneWithState(CEEState);
ExplodedNodeSet DstPostCall;
- getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode,
- *UpdatedCall, *this,
- /*WasInlined=*/true);
-
+ if (const CXXNewExpr *CNE = dyn_cast_or_null<CXXNewExpr>(CE)) {
+ ExplodedNodeSet DstPostPostCallCallback;
+ getCheckerManager().runCheckersForPostCall(DstPostPostCallCallback,
+ CEENode, *UpdatedCall, *this,
+ /*WasInlined=*/true);
+ for (auto I : DstPostPostCallCallback) {
+ getCheckerManager().runCheckersForNewAllocator(
+ CNE, getCXXNewAllocatorValue(I->getState(), CNE,
+ calleeCtx->getParent()),
+ DstPostCall, I, *this,
+ /*WasInlined=*/true);
+ }
+ } else {
+ getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode,
+ *UpdatedCall, *this,
+ /*WasInlined=*/true);
+ }
ExplodedNodeSet Dst;
if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) {
getCheckerManager().runCheckersForPostObjCMessage(Dst, DstPostCall, *Msg,
// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete,unix.Malloc -std=c++11 -fblocks -verify %s
-// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete,cplusplus.NewDeleteLeaks,unix.Malloc -std=c++11 -DLEAKS -fblocks -verify %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete,cplusplus.NewDeleteLeaks,unix.Malloc -std=c++11 -DLEAKS=1 -fblocks -verify %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete,unix.Malloc -std=c++11 -analyzer-config c++-allocator-inlining=true -DALLOCATOR_INLINING=1 -fblocks -verify %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete,cplusplus.NewDeleteLeaks,unix.Malloc -std=c++11 -analyzer-config c++-allocator-inlining=true -DLEAKS=1 -DALLOCATOR_INLINING=1 -fblocks -verify %s
#include "Inputs/system-header-simulator-cxx.h"
-#ifndef LEAKS
+#if !LEAKS
// expected-no-diagnostics
#endif
C *c3 = ::new C;
}
-#ifdef LEAKS
+#if LEAKS && !ALLOCATOR_INLINING
// expected-warning@-2{{Potential leak of memory pointed to by 'c3'}}
#endif
void testNewExprArray() {
int *p = new int[0];
}
-#ifdef LEAKS
+#if LEAKS && !ALLOCATOR_INLINING
// expected-warning@-2{{Potential leak of memory pointed to by 'p'}}
#endif
void testNewExpr() {
int *p = new int;
}
-#ifdef LEAKS
+#if LEAKS && !ALLOCATOR_INLINING
// expected-warning@-2{{Potential leak of memory pointed to by 'p'}}
#endif
void testOpNewNoThrow() {
void *p = operator new(0, std::nothrow);
}
-#ifdef LEAKS
+#if LEAKS
// expected-warning@-2{{Potential leak of memory pointed to by 'p'}}
#endif
void testNewExprNoThrow() {
int *p = new(std::nothrow) int;
}
-#ifdef LEAKS
+#if LEAKS
// expected-warning@-2{{Potential leak of memory pointed to by 'p'}}
#endif
//----- Custom placement operators
void testOpNewPlacement() {
void *p = operator new(0, 0.1); // no warn
-}
+}
void testNewExprPlacement() {
int *p = new(0.1) int; // no warn
--- /dev/null
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection,unix.Malloc -analyzer-config c++-allocator-inlining=true -std=c++11 -verify %s
+
+void clang_analyzer_eval(bool);
+
+typedef __typeof__(sizeof(int)) size_t;
+
+void *malloc(size_t size);
+
+void *operator new(size_t size) throw() {
+ void *x = malloc(size);
+ if (!x)
+ return nullptr;
+ return x;
+}
+
+void checkNewAndConstructorInlining() {
+ int *s = new int;
+} // expected-warning {{Potential leak of memory pointed to by 's'}}