diff --git a/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp --- a/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp @@ -30,7 +30,9 @@ using namespace taint; namespace { -class ArrayBoundCheckerV2 : public Checker { +class ArrayBoundCheckerV2 + : public Checker, + check::PostStmt> { mutable std::unique_ptr BT; mutable std::unique_ptr TaintBT; @@ -46,9 +48,9 @@ void impl(SVal Loc, bool isLoad, const Stmt *S, CheckerContext &C) const; public: - void checkLocation(SVal l, bool isLoad, const Stmt *S, - CheckerContext &C) const; void checkBind(SVal Loc, SVal, const Stmt *S, CheckerContext &) const; + void checkPostStmt(const ArraySubscriptExpr *E, CheckerContext &C) const; + void checkPostStmt(const UnaryOperator *E, CheckerContext &C) const; }; // FIXME: Eventually replace RegionRawOffset with this class. @@ -142,13 +144,6 @@ return {nullptr, nullptr}; } -void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, - const Stmt* LoadS, - CheckerContext &checkerContext) const { - if (isLoad) - impl(location, isLoad, LoadS, checkerContext); -} - void ArrayBoundCheckerV2::checkBind(SVal Loc, SVal, const Stmt *S, CheckerContext &C) const { impl(Loc, /*isLoad=*/false, S, C); @@ -375,6 +370,21 @@ return std::nullopt; } +void ArrayBoundCheckerV2::checkPostStmt(const ArraySubscriptExpr *E, + CheckerContext &C) const { + // TODO: Specialize the diagnostic message. + // It might not be a "load" operation. + impl(C.getSVal(E), /*isLoad=*/true, E, C); +} +void ArrayBoundCheckerV2::checkPostStmt(const UnaryOperator *E, + CheckerContext &C) const { + if (E->getOpcode() != UO_Deref) + return; + // TODO: Specialize the diagnostic message. + // It might not be a "load" operation. + impl(C.getSVal(E), /*isLoad=*/true, E, C); +} + void ento::registerArrayBoundCheckerV2(CheckerManager &mgr) { mgr.registerChecker(); } diff --git a/clang/test/Analysis/out-of-bounds-new.cpp b/clang/test/Analysis/out-of-bounds-new.cpp --- a/clang/test/Analysis/out-of-bounds-new.cpp +++ b/clang/test/Analysis/out-of-bounds-new.cpp @@ -176,3 +176,63 @@ break; } } + +void test_memberwise_copy() { + ManyInts dst[1]; + ManyInts src[2] = {5}; + + dst[0] = src[0]; // ok + dst[0].a = src[0].a; // ok + + // Positive indexing: + switch (rng()) { + default: break; + case 0: { + auto &z = dst[1]; // expected-warning {{Out of bound memory access (access exceeds upper limit of memory block)}} + break; + } + case 1: { + dst[1].a = src[1].a; // expected-warning {{Out of bound memory access (access exceeds upper limit of memory block)}} + break; + } + case 2: { + auto &z = *(dst + 1); // expected-warning {{Out of bound memory access (access exceeds upper limit of memory block)}} + break; + } + case 3: { + auto &z = *dst; + auto &zz = 1[&z]; // expected-warning {{Out of bound memory access (access exceeds upper limit of memory block)}} + break; + } + case 4: { + dst[1] = src[1]; // expected-warning {{Out of bound memory access (access exceeds upper limit of memory block)}} + break; + } + } + + // Negative indexing: + switch (rng()) { + default: break; + case 0: { + auto &z = dst[-1]; // expected-warning {{Out of bound memory access (accessed memory precedes memory block)}} + break; + } + case 1: { + dst[-1].a = src[1].a; // expected-warning {{Out of bound memory access (accessed memory precedes memory block)}} + break; + } + case 2: { + auto &z = *(dst - 1); // expected-warning {{Out of bound memory access (accessed memory precedes memory block)}} + break; + } + case 3: { + auto &z = *dst; + auto &zz = (-1)[&z]; // expected-warning {{Out of bound memory access (accessed memory precedes memory block)}} + break; + } + case 4: { + dst[-1] = src[1]; // expected-warning {{Out of bound memory access (accessed memory precedes memory block)}} + break; + } + } +}