[analyzer] Warn if the size of the array in `new[]` is undefined
This patch introduces a new checker, called NewArraySize checker, which detects if the expression that yields the element count of the array in new[], results in an Undefined value. Differential Revision: https://reviews.llvm.org/D131299
This commit is contained in:
parent
4f688d00f4
commit
a46154cb1c
|
@ -245,6 +245,22 @@ Check for uninitialized values being returned to the caller.
|
|||
return x; // warn
|
||||
}
|
||||
|
||||
.. _core-uninitialized-NewArraySize:
|
||||
|
||||
core.uninitialized.NewArraySize (C++)
|
||||
"""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Check if the element count in new[] is garbage or undefined.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
void test() {
|
||||
int n;
|
||||
int *arr = new int[n]; // warn: Element count in new[] is a garbage value
|
||||
delete[] arr;
|
||||
}
|
||||
|
||||
|
||||
.. _cplusplus-checkers:
|
||||
|
||||
|
||||
|
|
|
@ -437,6 +437,10 @@ def ReturnUndefChecker : Checker<"UndefReturn">,
|
|||
HelpText<"Check for uninitialized values being returned to the caller">,
|
||||
Documentation<HasDocumentation>;
|
||||
|
||||
def UndefinedNewArraySizeChecker : Checker<"NewArraySize">,
|
||||
HelpText<"Check if the size of the array in a new[] expression is undefined">,
|
||||
Documentation<HasDocumentation>;
|
||||
|
||||
} // end "core.uninitialized"
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -1033,6 +1033,18 @@ public:
|
|||
return getOriginExpr()->getNumPlacementArgs() + getNumImplicitArgs();
|
||||
}
|
||||
|
||||
bool isArray() const { return getOriginExpr()->isArray(); }
|
||||
|
||||
Optional<const clang::Expr *> getArraySizeExpr() const {
|
||||
return getOriginExpr()->getArraySize();
|
||||
}
|
||||
|
||||
SVal getArraySizeVal() const {
|
||||
assert(isArray() && "The allocator call doesn't allocate and array!");
|
||||
|
||||
return getState()->getSVal(*getArraySizeExpr(), getLocationContext());
|
||||
}
|
||||
|
||||
const Expr *getArgExpr(unsigned Index) const override {
|
||||
// The first argument of an allocator call is the size of the allocation.
|
||||
if (Index < getNumImplicitArgs())
|
||||
|
|
|
@ -120,6 +120,7 @@ add_clang_library(clangStaticAnalyzerCheckers
|
|||
UndefResultChecker.cpp
|
||||
UndefinedArraySubscriptChecker.cpp
|
||||
UndefinedAssignmentChecker.cpp
|
||||
UndefinedNewArraySizeChecker.cpp
|
||||
UninitializedObject/UninitializedObjectChecker.cpp
|
||||
UninitializedObject/UninitializedPointee.cpp
|
||||
UnixAPIChecker.cpp
|
||||
|
|
|
@ -1733,6 +1733,10 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
|
|||
// Fill the region with the initialization value.
|
||||
State = State->bindDefaultInitial(RetVal, Init, LCtx);
|
||||
|
||||
// If Size is somehow undefined at this point, this line prevents a crash.
|
||||
if (Size.isUndef())
|
||||
Size = UnknownVal();
|
||||
|
||||
// Set the region's extent.
|
||||
State = setDynamicExtent(State, RetVal.getAsRegion(),
|
||||
Size.castAs<DefinedOrUnknownSVal>(), svalBuilder);
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
//===--- UndefinedNewArraySizeChecker.cpp -----------------------*- C++ -*--==//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This defines UndefinedNewArraySizeChecker, a builtin check in ExprEngine
|
||||
// that checks if the size of the array in a new[] expression is undefined.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
|
||||
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
|
||||
#include "clang/StaticAnalyzer/Core/Checker.h"
|
||||
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
|
||||
|
||||
using namespace clang;
|
||||
using namespace ento;
|
||||
|
||||
namespace {
|
||||
class UndefinedNewArraySizeChecker : public Checker<check::PreCall> {
|
||||
|
||||
private:
|
||||
BugType BT{this, "Undefined array element count in new[]",
|
||||
categories::LogicError};
|
||||
|
||||
public:
|
||||
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
|
||||
void HandleUndefinedArrayElementCount(CheckerContext &C, SVal ArgVal,
|
||||
const Expr *Init,
|
||||
SourceRange Range) const;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void UndefinedNewArraySizeChecker::checkPreCall(const CallEvent &Call,
|
||||
CheckerContext &C) const {
|
||||
if (const auto *AC = dyn_cast<CXXAllocatorCall>(&Call)) {
|
||||
if (!AC->isArray())
|
||||
return;
|
||||
|
||||
auto *SizeEx = *AC->getArraySizeExpr();
|
||||
auto SizeVal = AC->getArraySizeVal();
|
||||
|
||||
if (SizeVal.isUndef())
|
||||
HandleUndefinedArrayElementCount(C, SizeVal, SizeEx,
|
||||
SizeEx->getSourceRange());
|
||||
}
|
||||
}
|
||||
|
||||
void UndefinedNewArraySizeChecker::HandleUndefinedArrayElementCount(
|
||||
CheckerContext &C, SVal ArgVal, const Expr *Init, SourceRange Range) const {
|
||||
|
||||
if (ExplodedNode *N = C.generateErrorNode()) {
|
||||
|
||||
SmallString<100> buf;
|
||||
llvm::raw_svector_ostream os(buf);
|
||||
|
||||
os << "Element count in new[] is a garbage value";
|
||||
|
||||
auto R = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N);
|
||||
R->markInteresting(ArgVal);
|
||||
R->addRange(Range);
|
||||
bugreporter::trackExpressionValue(N, Init, *R);
|
||||
|
||||
C.emitReport(std::move(R));
|
||||
}
|
||||
}
|
||||
|
||||
void ento::registerUndefinedNewArraySizeChecker(CheckerManager &mgr) {
|
||||
mgr.registerChecker<UndefinedNewArraySizeChecker>();
|
||||
}
|
||||
|
||||
bool ento::shouldRegisterUndefinedNewArraySizeChecker(
|
||||
const CheckerManager &mgr) {
|
||||
return mgr.getLangOpts().CPlusPlus;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s
|
||||
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection -verify %s
|
||||
|
||||
void clang_analyzer_warnIfReached();
|
||||
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
// RUN: %clang_analyze_cc1 %s -analyzer-checker=core.uninitialized.NewArraySize -analyzer-output=text -verify
|
||||
|
||||
#include "Inputs/system-header-simulator-cxx.h"
|
||||
|
||||
void checkUndefinedElmenetCountValue() {
|
||||
int n;
|
||||
// expected-note@-1{{'n' declared without an initial value}}
|
||||
|
||||
int *arr = new int[n]; // expected-warning{{Element count in new[] is a garbage value}}
|
||||
// expected-note@-1{{Element count in new[] is a garbage value}}
|
||||
}
|
||||
|
||||
void checkUndefinedElmenetCountMultiDimensionalValue() {
|
||||
int n;
|
||||
// expected-note@-1{{'n' declared without an initial value}}
|
||||
|
||||
auto *arr = new int[n][5]; // expected-warning{{Element count in new[] is a garbage value}}
|
||||
// expected-note@-1{{Element count in new[] is a garbage value}}
|
||||
}
|
||||
|
||||
void checkUndefinedElmenetCountReference() {
|
||||
int n;
|
||||
// expected-note@-1{{'n' declared without an initial value}}
|
||||
|
||||
int &ref = n;
|
||||
// expected-note@-1{{'ref' initialized here}}
|
||||
|
||||
int *arr = new int[ref]; // expected-warning{{Element count in new[] is a garbage value}}
|
||||
// expected-note@-1{{Element count in new[] is a garbage value}}
|
||||
}
|
||||
|
||||
void checkUndefinedElmenetCountMultiDimensionalReference() {
|
||||
int n;
|
||||
// expected-note@-1{{'n' declared without an initial value}}
|
||||
|
||||
int &ref = n;
|
||||
// expected-note@-1{{'ref' initialized here}}
|
||||
|
||||
auto *arr = new int[ref][5]; // expected-warning{{Element count in new[] is a garbage value}}
|
||||
// expected-note@-1{{Element count in new[] is a garbage value}}
|
||||
}
|
||||
|
||||
int foo() {
|
||||
int n;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void checkUndefinedElmenetCountFunction() {
|
||||
int *arr = new int[foo()]; // expected-warning{{Element count in new[] is a garbage value}}
|
||||
// expected-note@-1{{Element count in new[] is a garbage value}}
|
||||
}
|
||||
|
||||
void checkUndefinedElmenetCountMultiDimensionalFunction() {
|
||||
auto *arr = new int[foo()][5]; // expected-warning{{Element count in new[] is a garbage value}}
|
||||
// expected-note@-1{{Element count in new[] is a garbage value}}
|
||||
}
|
||||
|
||||
void *malloc(size_t);
|
||||
|
||||
void checkUndefinedPlacementElementCount() {
|
||||
int n;
|
||||
// expected-note@-1{{'n' declared without an initial value}}
|
||||
|
||||
void *buffer = malloc(sizeof(std::string) * 10);
|
||||
std::string *p =
|
||||
::new (buffer) std::string[n]; // expected-warning{{Element count in new[] is a garbage value}}
|
||||
// expected-note@-1{{Element count in new[] is a garbage value}}
|
||||
}
|
Loading…
Reference in New Issue