Index: clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -331,6 +331,8 @@ void checkPreCall(const CallEvent &Call, CheckerContext &C) const; void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + void checkUndefinedNewArraySize(const CallEvent &Call, + CheckerContext &C) const; void checkNewAllocator(const CXXAllocatorCall &Call, CheckerContext &C) const; void checkPostObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const; void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; @@ -362,6 +364,7 @@ mutable std::unique_ptr BT_BadFree[CK_NumCheckKinds]; mutable std::unique_ptr BT_FreeAlloca[CK_NumCheckKinds]; mutable std::unique_ptr BT_MismatchedDealloc; + mutable std::unique_ptr BT_UndefinedArrayElementCount; mutable std::unique_ptr BT_OffsetFree[CK_NumCheckKinds]; mutable std::unique_ptr BT_UseZerroAllocated[CK_NumCheckKinds]; @@ -703,6 +706,10 @@ void HandleFreeAlloca(CheckerContext &C, SVal ArgVal, SourceRange Range) const; + void HandleUndefinedArrayElementCount(CheckerContext &C, SVal ArgVal, + const Expr *Init, SourceRange Range, + AllocationFamily Family) const; + void HandleMismatchedDealloc(CheckerContext &C, SourceRange Range, const Expr *DeallocExpr, const RefState *RS, SymbolRef Sym, bool OwnershipTransferred) const; @@ -1496,6 +1503,7 @@ } if (isStandardNewDelete(Call)) { + checkUndefinedNewArraySize(Call, C); checkCXXNewOrCXXDelete(Call, C); return; } @@ -1503,6 +1511,40 @@ checkOwnershipAttr(Call, C); } +void MallocChecker::checkUndefinedNewArraySize(const CallEvent &Call, + CheckerContext &C) const { + const auto *NE = dyn_cast_or_null(Call.getOriginExpr()); + + if (!NE) + return; + + if (NE->isArray()) { + auto *SizeEx = NE->getArraySize().value_or(nullptr); + + assert(SizeEx && "new[] without a size!?"); + + // Prevent a crash if assertions are not enabled. + if (!SizeEx) + return; + + const auto State = Call.getState(); + const auto *LCtx = Call.getLocationContext(); + const auto Ty = SizeEx->getType(); + + auto SizeVal = State->getSVal(SizeEx, LCtx); + if (Ty->isReferenceType()) { + if (const auto *MR = SizeVal.getAsRegion()) + SizeVal = State->getSVal(SizeVal.getAsRegion()); + else + SizeVal = UnknownVal{}; + } + + if (SizeVal.isUndef()) + HandleUndefinedArrayElementCount( + C, SizeVal, SizeEx, SizeEx->getSourceRange(), AF_CXXNewArray); + } +} + // Performs a 0-sized allocations check. ProgramStateRef MallocChecker::ProcessZeroAllocCheck( const CallEvent &Call, const unsigned IndexOfSizeArg, ProgramStateRef State, @@ -1749,6 +1791,10 @@ // Fill the region with the initialization value. State = State->bindDefaultInitial(RetVal, Init, LCtx); + // If Size is somehow undefined at this point, this line prevent a crash. + if (Size.isUndef()) + Size = UnknownVal(); + // Set the region's extent. State = setDynamicExtent(State, RetVal.getAsRegion(), Size.castAs(), svalBuilder); @@ -2283,6 +2329,57 @@ } } +void MallocChecker::HandleUndefinedArrayElementCount( + CheckerContext &C, SVal ArgVal, const Expr *Init, SourceRange Range, + AllocationFamily Family) const { + if (!ChecksEnabled[CK_MallocChecker] && !ChecksEnabled[CK_NewDeleteChecker]) { + C.addSink(); + return; + } + + Optional CheckKind = getCheckIfTracked(Family); + if (!CheckKind) + return; + + if (ExplodedNode *N = C.generateErrorNode()) { + if (!BT_UndefinedArrayElementCount) + BT_UndefinedArrayElementCount.reset( + new BugType(CheckNames[*CheckKind], "Undefined array element count", + categories::MemoryError)); + + SmallString<100> buf; + llvm::raw_svector_ostream os(buf); + + os << "Undefined element count! "; + + // Try to be more informative... + os << "The value "; + Init = Init->IgnoreCasts(); + + if (const auto *DRE = dyn_cast(Init)) + os << "stored in '" << *DRE->getDecl() << "' "; + else if (const auto *CE = dyn_cast(Init)) { + os << "returned "; + + if (auto CalleeDecl = dyn_cast_or_null(CE->getCalleeDecl())) + os << "from '" << *CalleeDecl << "' "; + else + os << "'[*here*]' "; + } else + os << "'[*here*]' "; + + os << "is "; + ArgVal.dumpToStream(os); + os << "!"; + + auto R = std::make_unique( + *BT_UndefinedArrayElementCount, os.str(), N); + R->markInteresting(ArgVal); + R->addRange(Range); + C.emitReport(std::move(R)); + } +} + void MallocChecker::HandleMismatchedDealloc(CheckerContext &C, SourceRange Range, const Expr *DeallocExpr, Index: clang/test/Analysis/undefined-new-element.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/undefined-new-element.cpp @@ -0,0 +1,52 @@ +// RUN: %clang_analyze_cc1 %s \ +// RUN: -analyzer-checker=cplusplus \ +// RUN: -verify + +void checkUndefinedElmenetCountValue() +{ + int n; + + int *arr = new int[n]; // expected-warning{{Undefined element count!}} +} + +void checkUndefinedElmenetCountMultiDimensionalValue() +{ + int n; + + auto *arr = new int[n][5]; // expected-warning{{Undefined element count!}} +} + +void checkUndefinedElmenetCountReference() +{ + int n; + + int& ref = n; + + int *arr = new int[ref]; // expected-warning{{Undefined element count!}} +} + +void checkUndefinedElmenetCountMultiDimensionalReference() +{ + int n; + + int& ref = n; + + auto *arr = new int[ref][5]; // expected-warning{{Undefined element count!}} +} + +int foo() +{ + int n; + + return n; +} + +void checkUndefinedElmenetCountFunction() +{ + int *arr = new int[foo()]; // expected-warning{{Undefined element count!}} +} + +void checkUndefinedElmenetCountMultiDimensionalFunction() +{ + auto *arr = new int[foo()][5]; // expected-warning{{Undefined element count!}} +}