From: Chandler Carruth Date: Thu, 17 Feb 2011 21:10:52 +0000 (+0000) Subject: Enhance the array bounds checking to work for several other constructs, X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=35001ca261f895817916b468379b696d6d45959d;p=clang Enhance the array bounds checking to work for several other constructs, especially C++ code, and generally expand the test coverage. Logic adapted from a patch by Kaelyn Uhrain and another Googler. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@125775 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index d00f092274..6b1013d82d 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -3095,17 +3095,12 @@ void Sema::CheckCastAlign(Expr *Op, QualType T, SourceRange TRange) { } void Sema::CheckArrayAccess(const clang::ArraySubscriptExpr *E) { - const DeclRefExpr *DRE = - dyn_cast(E->getBase()->IgnoreParenImpCasts()); - if (!DRE) - return; - const VarDecl *Variable = dyn_cast(DRE->getDecl()); - if (!Variable) - return; + const Expr *BaseExpr = E->getBase()->IgnoreParenImpCasts(); const ConstantArrayType *ArrayTy = - Context.getAsConstantArrayType(Variable->getType()); + Context.getAsConstantArrayType(BaseExpr->getType()); if (!ArrayTy) return; + const Expr *IndexExpr = E->getIdx(); if (IndexExpr->isValueDependent()) return; @@ -3115,6 +3110,8 @@ void Sema::CheckArrayAccess(const clang::ArraySubscriptExpr *E) { if (!index.isNegative()) { const llvm::APInt &size = ArrayTy->getSize(); + if (!size.isStrictlyPositive()) + return; if (size.getBitWidth() > index.getBitWidth()) index = index.sext(size.getBitWidth()); if (index.slt(size)) @@ -3127,7 +3124,14 @@ void Sema::CheckArrayAccess(const clang::ArraySubscriptExpr *E) { Diag(E->getBase()->getLocStart(), diag::warn_array_index_precedes_bounds) << index.toString(10, true) << IndexExpr->getSourceRange(); } - Diag(Variable->getLocStart(), diag::note_array_index_out_of_bounds) - << Variable->getDeclName(); + + const NamedDecl *ND = NULL; + if (const DeclRefExpr *DRE = dyn_cast(BaseExpr)) + ND = dyn_cast(DRE->getDecl()); + if (const MemberExpr *ME = dyn_cast(BaseExpr)) + ND = dyn_cast(ME->getMemberDecl()); + if (ND) + Diag(ND->getLocStart(), diag::note_array_index_out_of_bounds) + << ND->getDeclName(); } diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 6fa22a9776..44508540d0 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -2005,6 +2005,10 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType, if (!From->isGLValue()) break; } + // Check for trivial buffer overflows. + if (const ArraySubscriptExpr *AE = dyn_cast(From)) + CheckArrayAccess(AE); + FromType = FromType.getUnqualifiedType(); From = ImplicitCastExpr::Create(Context, FromType, CK_LValueToRValue, From, 0, VK_RValue); diff --git a/test/Analysis/outofbound.c b/test/Analysis/outofbound.c index ed51dc6ac0..3b261bbb5c 100644 --- a/test/Analysis/outofbound.c +++ b/test/Analysis/outofbound.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -analyze -analyzer-experimental-internal-checks -analyzer-experimental-checks -analyzer-check-objc-mem -analyzer-store=region -verify %s +// RUN: %clang_cc1 -Wno-array-bounds -analyze -analyzer-experimental-internal-checks -analyzer-experimental-checks -analyzer-check-objc-mem -analyzer-store=region -verify %s typedef __typeof(sizeof(int)) size_t; void *malloc(size_t); diff --git a/test/Parser/encode.m b/test/Parser/encode.m index e0e7535081..7b8022e78d 100644 --- a/test/Parser/encode.m +++ b/test/Parser/encode.m @@ -1,8 +1,8 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s int main(void) { - const char ch = @encode(char *)[2]; - char c = @encode(char *)[2] + 4; + const char ch = @encode(char *)[0]; + char c = @encode(char *)[0] + 4; return c; } diff --git a/test/SemaCXX/array-bounds.cpp b/test/SemaCXX/array-bounds.cpp index d60600fd4b..94973762a1 100644 --- a/test/SemaCXX/array-bounds.cpp +++ b/test/SemaCXX/array-bounds.cpp @@ -5,17 +5,72 @@ int foo() { int y[2]; // expected-note 2 {{array 'y' declared here}} int *p = &y[2]; // no-warning (void) sizeof(x[2]); // no-warning - y[2] = 2; // expected-warning{{array index of '2' indexes past the end of an array (that contains 2 elements)}} - return x[2] + // expected-warning{{array index of '2' indexes past the end of an array (that contains 2 elements)}} - y[-1] + // expected-warning{{array index of '-1' indexes before the beginning of the array}} - x[sizeof(x)] + // expected-warning{{array index of '8' indexes past the end of an array (that contains 2 elements)}} - x[sizeof(x) / sizeof(x[0])] + // expected-warning{{array index of '2' indexes past the end of an array (that contains 2 elements)}} + y[2] = 2; // expected-warning {{array index of '2' indexes past the end of an array (that contains 2 elements)}} + return x[2] + // expected-warning {{array index of '2' indexes past the end of an array (that contains 2 elements)}} + y[-1] + // expected-warning {{array index of '-1' indexes before the beginning of the array}} + x[sizeof(x)] + // expected-warning {{array index of '8' indexes past the end of an array (that contains 2 elements)}} + x[sizeof(x) / sizeof(x[0])] + // expected-warning {{array index of '2' indexes past the end of an array (that contains 2 elements)}} x[sizeof(x) / sizeof(x[0]) - 1] + // no-warning - x[sizeof(x[2])]; // expected-warning{{array index of '4' indexes past the end of an array (that contains 2 elements)}} + x[sizeof(x[2])]; // expected-warning {{array index of '4' indexes past the end of an array (that contains 2 elements)}} } // This code example tests that -Warray-bounds works with arrays that // are template parameters. template class Qux { bool test() { return sz[0] == 'a'; } -}; \ No newline at end of file +}; + +void f1(int a[1]) { + int val = a[3]; // no warning for function argumnet +} + +void f2(const int (&a)[1]) { // expected-note {{declared here}} + int val = a[3]; // expected-warning {{array index of '3' indexes past the end of an array (that contains 1 elements)}} +} + +void test() { + struct { + int a[0]; + } s2; + s2.a[3] = 0; // no warning for 0-sized array + + union { + short a[2]; // expected-note {{declared here}} + char c[4]; + } u; + u.a[3] = 1; // expected-warning {{array index of '3' indexes past the end of an array (that contains 2 elements)}} + u.c[3] = 1; // no warning + + const int const_subscript = 3; + int array[1]; // expected-note {{declared here}} + array[const_subscript] = 0; // expected-warning {{array index of '3' indexes past the end of an array (that contains 1 elements)}} + + int *ptr; + ptr[3] = 0; // no warning for pointer references + int array2[] = { 0, 1, 2 }; // expected-note 2 {{declared here}} + + array2[3] = 0; // expected-warning {{array index of '3' indexes past the end of an array (that contains 3 elements)}} + array2[2+2] = 0; // expected-warning {{array index of '4' indexes past the end of an array (that contains 3 elements)}} + + const char *str1 = "foo"; + char c1 = str1[5]; // no warning for pointers + + const char str2[] = "foo"; // expected-note {{declared here}} + char c2 = str2[5]; // expected-warning {{array index of '5' indexes past the end of an array (that contains 4 elements)}} + + int (*array_ptr)[1]; + (*array_ptr)[3] = 1; // expected-warning {{array index of '3' indexes past the end of an array (that contains 1 elements)}} +} + +template struct S { + char arr[I]; // expected-note 3 {{declared here}} +}; +template void f() { + S<3> s; + s.arr[4] = 0; // expected-warning 2 {{array index of '4' indexes past the end of an array (that contains 3 elements)}} + s.arr[I] = 0; // expected-warning {{array index of '5' indexes past the end of an array (that contains 3 elements)}} +} + +void test_templates() { + f<5>(); // expected-note {{in instantiation}} +}