[clang] e48f444 - [Sema] Fix -Warray-bounds false negative when casting an out-of-bounds array item

via cfe-commits cfe-commits at lists.llvm.org
Wed Feb 3 05:51:14 PST 2021


Author: Ilya Mirsky
Date: 2021-02-03T07:50:50-06:00
New Revision: e48f444751cf781c42934b242b81f549da77bad0

URL: https://github.com/llvm/llvm-project/commit/e48f444751cf781c42934b242b81f549da77bad0
DIFF: https://github.com/llvm/llvm-project/commit/e48f444751cf781c42934b242b81f549da77bad0.diff

LOG: [Sema] Fix -Warray-bounds false negative when casting an out-of-bounds array item

Patch by Ilya Mirsky!

Fixes: http://llvm.org/PR44343

Reviewed By: rsmith

Differential Revision: https://reviews.llvm.org/D71714

Added: 
    

Modified: 
    clang/include/clang/Sema/Sema.h
    clang/lib/Sema/SemaChecking.cpp
    clang/test/Parser/cxx-ambig-decl-expr.cpp
    clang/test/SemaCXX/array-bounds.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 7f7c84eb1b1d..2fca81d25345 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -12310,7 +12310,7 @@ class Sema final {
   void CheckArrayAccess(const Expr *BaseExpr, const Expr *IndexExpr,
                         const ArraySubscriptExpr *ASE=nullptr,
                         bool AllowOnePastEnd=true, bool IndexNegated=false);
-  void CheckArrayAccess(const Expr *E);
+  void CheckArrayAccess(const Expr *E, int AllowOnePastEnd = 0);
   // Used to grab the relevant information from a FormatAttr and a
   // FunctionDeclaration.
   struct FormatStringInfo {

diff  --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 2d3d36f4adad..17dd5c9e8d99 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -14387,62 +14387,63 @@ void Sema::CheckArrayAccess(const Expr *BaseExpr, const Expr *IndexExpr,
                         PDiag(diag::note_array_declared_here) << ND);
 }
 
-void Sema::CheckArrayAccess(const Expr *expr) {
-  int AllowOnePastEnd = 0;
-  while (expr) {
-    expr = expr->IgnoreParenImpCasts();
-    switch (expr->getStmtClass()) {
-      case Stmt::ArraySubscriptExprClass: {
-        const ArraySubscriptExpr *ASE = cast<ArraySubscriptExpr>(expr);
-        CheckArrayAccess(ASE->getBase(), ASE->getIdx(), ASE,
-                         AllowOnePastEnd > 0);
-        expr = ASE->getBase();
-        break;
-      }
-      case Stmt::MemberExprClass: {
-        expr = cast<MemberExpr>(expr)->getBase();
-        break;
-      }
-      case Stmt::OMPArraySectionExprClass: {
-        const OMPArraySectionExpr *ASE = cast<OMPArraySectionExpr>(expr);
-        if (ASE->getLowerBound())
-          CheckArrayAccess(ASE->getBase(), ASE->getLowerBound(),
-                           /*ASE=*/nullptr, AllowOnePastEnd > 0);
-        return;
-      }
-      case Stmt::UnaryOperatorClass: {
-        // Only unwrap the * and & unary operators
-        const UnaryOperator *UO = cast<UnaryOperator>(expr);
-        expr = UO->getSubExpr();
-        switch (UO->getOpcode()) {
-          case UO_AddrOf:
-            AllowOnePastEnd++;
-            break;
-          case UO_Deref:
-            AllowOnePastEnd--;
-            break;
-          default:
-            return;
-        }
-        break;
-      }
-      case Stmt::ConditionalOperatorClass: {
-        const ConditionalOperator *cond = cast<ConditionalOperator>(expr);
-        if (const Expr *lhs = cond->getLHS())
-          CheckArrayAccess(lhs);
-        if (const Expr *rhs = cond->getRHS())
-          CheckArrayAccess(rhs);
-        return;
-      }
-      case Stmt::CXXOperatorCallExprClass: {
-        const auto *OCE = cast<CXXOperatorCallExpr>(expr);
-        for (const auto *Arg : OCE->arguments())
-          CheckArrayAccess(Arg);
-        return;
-      }
-      default:
-        return;
+void Sema::CheckArrayAccess(const Expr *expr, int AllowOnePastEnd) {
+  if (!expr)
+    return;
+
+  expr = expr->IgnoreParenCasts();
+  switch (expr->getStmtClass()) {
+  case Stmt::ArraySubscriptExprClass: {
+    const ArraySubscriptExpr *ASE = cast<ArraySubscriptExpr>(expr);
+    CheckArrayAccess(ASE->getBase(), ASE->getIdx(), ASE, AllowOnePastEnd > 0);
+    CheckArrayAccess(ASE->getBase(), AllowOnePastEnd);
+    return;
+  }
+  case Stmt::MemberExprClass: {
+    expr = cast<MemberExpr>(expr)->getBase();
+    CheckArrayAccess(expr, /*AllowOnePastEnd=*/0);
+    return;
+  }
+  case Stmt::OMPArraySectionExprClass: {
+    const OMPArraySectionExpr *ASE = cast<OMPArraySectionExpr>(expr);
+    if (ASE->getLowerBound())
+      CheckArrayAccess(ASE->getBase(), ASE->getLowerBound(),
+                       /*ASE=*/nullptr, AllowOnePastEnd > 0);
+    return;
+  }
+  case Stmt::UnaryOperatorClass: {
+    // Only unwrap the * and & unary operators
+    const UnaryOperator *UO = cast<UnaryOperator>(expr);
+    expr = UO->getSubExpr();
+    switch (UO->getOpcode()) {
+    case UO_AddrOf:
+      AllowOnePastEnd++;
+      break;
+    case UO_Deref:
+      AllowOnePastEnd--;
+      break;
+    default:
+      return;
     }
+    CheckArrayAccess(expr, AllowOnePastEnd);
+    return;
+  }
+  case Stmt::ConditionalOperatorClass: {
+    const ConditionalOperator *cond = cast<ConditionalOperator>(expr);
+    if (const Expr *lhs = cond->getLHS())
+      CheckArrayAccess(lhs, AllowOnePastEnd);
+    if (const Expr *rhs = cond->getRHS())
+      CheckArrayAccess(rhs, AllowOnePastEnd);
+    return;
+  }
+  case Stmt::CXXOperatorCallExprClass: {
+    const auto *OCE = cast<CXXOperatorCallExpr>(expr);
+    for (const auto *Arg : OCE->arguments())
+      CheckArrayAccess(Arg);
+    return;
+  }
+  default:
+    return;
   }
 }
 

diff  --git a/clang/test/Parser/cxx-ambig-decl-expr.cpp b/clang/test/Parser/cxx-ambig-decl-expr.cpp
index 6203db2fbd22..373fe250c6b5 100644
--- a/clang/test/Parser/cxx-ambig-decl-expr.cpp
+++ b/clang/test/Parser/cxx-ambig-decl-expr.cpp
@@ -24,7 +24,7 @@ void arr() {
 
   // This is array indexing not an array declarator because a comma expression
   // is not syntactically a constant-expression.
-  int(x[1,1]); // expected-warning 2{{unused}}
+  int(x[1,0]); // expected-warning 2{{unused}}
 
   // This is array indexing not an array declaration because a braced-init-list
   // is not syntactically a constant-expression.

diff  --git a/clang/test/SemaCXX/array-bounds.cpp b/clang/test/SemaCXX/array-bounds.cpp
index 47be6c2423dc..837175014fe5 100644
--- a/clang/test/SemaCXX/array-bounds.cpp
+++ b/clang/test/SemaCXX/array-bounds.cpp
@@ -27,7 +27,7 @@ template <char *sz> class Qux {
 };
 
 void f1(int a[1]) {
-  int val = a[3]; // no warning for function argumnet
+  int val = a[3]; // no warning for function argument
 }
 
 void f2(const int (&a)[2]) { // expected-note {{declared here}}
@@ -133,7 +133,7 @@ int test_pr9296() {
 
 int test_sizeof_as_condition(int flag) {
   int arr[2] = { 0, 0 }; // expected-note {{array 'arr' declared here}}
-  if (flag) 
+  if (flag)
     return sizeof(char) != sizeof(char) ? arr[2] : arr[1];
   return sizeof(char) == sizeof(char) ? arr[2] : arr[1]; // expected-warning {{array index 2 is past the end of the array (which contains 2 elements)}}
 }
@@ -241,7 +241,7 @@ void test_pr10771() {
 }
 
 int test_pr11007_aux(const char * restrict, ...);
-  
+
 // Test checking with varargs.
 void test_pr11007() {
   double a[5]; // expected-note {{array 'a' declared here}}
@@ -320,3 +320,33 @@ void test() {
   arr<float>[1] = 0; // expected-warning {{array index 1 is past the end of the array (which contains 1 element)}}
 }
 } // namespace var_template_array
+
+namespace PR44343 {
+  const unsigned int array[2] = {0, 1}; // expected-note 5{{array 'array' declared here}}
+
+  const int i1 = (const int)array[2]; // expected-warning {{array index 2 is past the end of the array (which contains 2 elements)}}
+  const int i2 = static_cast<const int>(array[2]); // expected-warning {{array index 2 is past the end of the array (which contains 2 elements)}}
+  const int &i3 = reinterpret_cast<const int&>(array[2]); // expected-warning {{array index 2 is past the end of the array (which contains 2 elements)}}
+  unsigned int &i4 = const_cast<unsigned int&>(array[2]); // expected-warning {{array index 2 is past the end of the array (which contains 2 elements)}}
+  int i5 = int(array[2]); // expected-warning {{array index 2 is past the end of the array (which contains 2 elements)}}
+  const unsigned int *i6 = &(1 > 0 ? array[2] : array[1]); // no warning for one-past-end element's address retrieval
+
+  // Test dynamic cast
+  struct Base {
+    virtual ~Base();
+  };
+  struct Derived : Base {
+  };
+  Base baseArr[2]; // expected-note {{array 'baseArr' declared here}}
+  Derived *d1 = dynamic_cast<Derived *>(&baseArr[2]); // FIXME: Should actually warn because dynamic_cast accesses the vptr
+  Derived &d2 = dynamic_cast<Derived &>(baseArr[2]); // expected-warning {{array index 2 is past the end of the array (which contains 2 elements)}}
+
+  // Test operator `&` in combination with operators `.` and `->`
+  struct A {
+    int n;
+  };
+  A a[2]; // expected-note {{array 'a' declared here}}
+  int *n = &a[2].n; // expected-warning {{array index 2 is past the end of the array (which contains 2 elements)}}
+  A *aPtr[2]; // expected-note {{array 'aPtr' declared here}}
+  int *n2 = &aPtr[2]->n; // expected-warning {{array index 2 is past the end of the array (which contains 2 elements)}}
+}


        


More information about the cfe-commits mailing list