Index: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -605,6 +605,8 @@ bool inlineCall(const CallEvent &Call, const Decl *D, NodeBuilder &Bldr, ExplodedNode *Pred, ProgramStateRef State); + static bool isStandardGlobalOperatorNewFunction(const CXXNewExpr *CNE); + /// \brief Conservatively evaluate call by invalidating regions and binding /// a conjured return value. void conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr, Index: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -453,8 +453,10 @@ ExplodedNodeSet DstPostCall; StmtNodeBuilder CallBldr(DstPreCall, DstPostCall, *currBldrCtx); - for (auto I : DstPreCall) + for (auto I : DstPreCall) { + // FIXME: Provide evalCall for checkers? defaultEvalCall(CallBldr, I, *Call); + } // Store return value of operator new() for future use, until the actual // CXXNewExpr gets processed. @@ -471,6 +473,22 @@ *Call, *this); } +bool ExprEngine::isStandardGlobalOperatorNewFunction(const CXXNewExpr *CNE) { + const FunctionDecl *FD = CNE->getOperatorNew(); + if (FD && !isa(FD) && !FD->isVariadic()) { + if (FD->getNumParams() == 2) { + QualType T = FD->getParamDecl(1)->getType(); + if (const IdentifierInfo *II = T.getBaseTypeIdentifier()) { + // NoThrow placement new behaves as a standard new. + return II->getName().equals("nothrow_t"); + } + } else { + // Placement forms are considered non-standard. + return FD->getNumParams() == 1; + } + } + return false; +} void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { @@ -484,18 +502,7 @@ SVal symVal = UnknownVal(); FunctionDecl *FD = CNE->getOperatorNew(); - bool IsStandardGlobalOpNewFunction = false; - if (FD && !isa(FD) && !FD->isVariadic()) { - if (FD->getNumParams() == 2) { - QualType T = FD->getParamDecl(1)->getType(); - if (const IdentifierInfo *II = T.getBaseTypeIdentifier()) - // NoThrow placement new behaves as a standard new. - IsStandardGlobalOpNewFunction = II->getName().equals("nothrow_t"); - } - else - // Placement forms are considered non-standard. - IsStandardGlobalOpNewFunction = (FD->getNumParams() == 1); - } + bool IsStandardGlobalOpNewFunction = isStandardGlobalOperatorNewFunction(CNE); ProgramStateRef State = Pred->getState(); @@ -546,16 +553,20 @@ StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); + SVal Result = symVal; + if (CNE->isArray()) { // FIXME: allocating an array requires simulating the constructors. // For now, just return a symbolicated region. - const SubRegion *NewReg = - symVal.castAs().getRegionAs(); - QualType ObjTy = CNE->getType()->getAs()->getPointeeType(); - const ElementRegion *EleReg = - getStoreManager().GetElementZeroRegion(NewReg, ObjTy); - State = State->BindExpr(CNE, Pred->getLocationContext(), - loc::MemRegionVal(EleReg)); + if (!AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) { + const SubRegion *NewReg = + symVal.castAs().getRegionAs(); + QualType ObjTy = CNE->getType()->getAs()->getPointeeType(); + const ElementRegion *EleReg = + getStoreManager().GetElementZeroRegion(NewReg, ObjTy); + Result = loc::MemRegionVal(EleReg); + } + State = State->BindExpr(CNE, Pred->getLocationContext(), Result); Bldr.generateNode(CNE, Pred, State); return; } @@ -564,7 +575,6 @@ // CXXNewExpr, we need to make sure that the constructed object is not // immediately invalidated here. (The placement call should happen before // the constructor call anyway.) - SVal Result = symVal; if (FD && FD->isReservedGlobalPlacementOperator()) { // Non-array placement new should always return the placement location. SVal PlacementLoc = State->getSVal(CNE->getPlacementArg(0), LCtx); Index: lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -562,7 +562,19 @@ QualType ResultTy = Call.getResultType(); SValBuilder &SVB = getSValBuilder(); unsigned Count = currBldrCtx->blockCount(); - SVal R = SVB.conjureSymbolVal(nullptr, E, LCtx, ResultTy, Count); + + // See if we need to conjure a heap pointer instead of + // a regular unknown pointer. + bool IsHeapPointer = false; + if (const auto *CNE = dyn_cast(E)) + if (isStandardGlobalOperatorNewFunction(CNE)) { + // FIXME: Delegate this to evalCall in MallocChecker? + IsHeapPointer = true; + } + + SVal R = IsHeapPointer + ? SVB.getConjuredHeapSymbolVal(E, LCtx, Count) + : SVB.conjureSymbolVal(nullptr, E, LCtx, ResultTy, Count); return State->BindExpr(E, LCtx, R); } Index: test/Analysis/NewDelete-checker-test.cpp =================================================================== --- test/Analysis/NewDelete-checker-test.cpp +++ test/Analysis/NewDelete-checker-test.cpp @@ -1,5 +1,7 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete -std=c++11 -fblocks -verify %s // RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDeleteLeaks -DLEAKS -std=c++11 -fblocks -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete -std=c++11 -fblocks -analyzer-config c++-allocator-inlining=true -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDeleteLeaks -DLEAKS -std=c++11 -fblocks -analyzer-config c++-allocator-inlining=true -verify %s #include "Inputs/system-header-simulator-cxx.h" typedef __typeof__(sizeof(int)) size_t; Index: test/Analysis/new-ctor-conservative.cpp =================================================================== --- test/Analysis/new-ctor-conservative.cpp +++ test/Analysis/new-ctor-conservative.cpp @@ -21,3 +21,9 @@ int *k = new int(5); clang_analyzer_eval(*k == 5); // expected-warning{{TRUE}} } + +void checkNewArray() { + S *s = new S[10]; + // FIXME: Should be true once we inline array constructors. + clang_analyzer_eval(s[0].x == 1); // expected-warning{{UNKNOWN}} +} Index: test/Analysis/new-ctor-null.cpp =================================================================== --- /dev/null +++ test/Analysis/new-ctor-null.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config c++-allocator-inlining=true -std=c++11 -verify %s + +void clang_analyzer_eval(bool); + +typedef __typeof__(sizeof(int)) size_t; + +void *operator new(size_t size) throw() { + return nullptr; +} +void *operator new[](size_t size) throw() { + return nullptr; +} + +struct S { + int x; + S() : x(1) {} + ~S() {} +}; + +void testArrays() { + S *s = new S[10]; // no-crash + s[0].x = 2; // expected-warning{{Dereference of null pointer}} +}