diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -207,7 +207,6 @@ ASTContext &Context, LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel, bool IgnoreTemplateOrMacroSubstitution) const { - // For compatibility with existing code, we treat arrays of length 0 or // 1 as flexible array members. if (const auto *CAT = Context.getAsConstantArrayType(getType())) { @@ -217,13 +216,16 @@ if (Size == 0) return true; + using FAMKind = LangOptions::StrictFlexArraysLevelKind; + // FIXME: While the default -fstrict-flex-arrays=0 permits Size>1 trailing // arrays to be treated as flexible-array-members, we still emit diagnostics // as if they are not. Pending further discussion... - using FAMKind = LangOptions::StrictFlexArraysLevelKind; - if (StrictFlexArraysLevel == FAMKind::ZeroOrIncomplete || Size.uge(2)) + if (StrictFlexArraysLevel == FAMKind::ZeroOrIncomplete && Size.uge(1)) return false; + if (StrictFlexArraysLevel == FAMKind::OneZeroOrIncomplete && Size.uge(2)) + return false; } else if (!Context.getAsIncompleteArrayType(getType())) return false; diff --git a/clang/test/CodeGen/bounds-checking-fam.c b/clang/test/CodeGen/bounds-checking-fam.c --- a/clang/test/CodeGen/bounds-checking-fam.c +++ b/clang/test/CodeGen/bounds-checking-fam.c @@ -1,14 +1,18 @@ // REQUIRES: x86-registered-target -// RUN: %clang_cc1 -emit-llvm -triple x86_64 -fstrict-flex-arrays=0 -fsanitize=array-bounds %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-STRICT-0 -// RUN: %clang_cc1 -emit-llvm -triple x86_64 -fstrict-flex-arrays=0 -fsanitize=array-bounds -x c++ %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-STRICT-0,CXX,CXX-STRICT-0 -// RUN: %clang_cc1 -emit-llvm -triple x86_64 -fstrict-flex-arrays=1 -fsanitize=array-bounds %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-STRICT-1 -// RUN: %clang_cc1 -emit-llvm -triple x86_64 -fstrict-flex-arrays=1 -fsanitize=array-bounds -x c++ %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-STRICT-1,CXX -// RUN: %clang_cc1 -emit-llvm -triple x86_64 -fstrict-flex-arrays=2 -fsanitize=array-bounds %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-STRICT-2 -// RUN: %clang_cc1 -emit-llvm -triple x86_64 -fstrict-flex-arrays=2 -fsanitize=array-bounds -x c++ %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-STRICT-2,CXX +// RUN: %clang_cc1 -O2 -emit-llvm -triple x86_64 -fstrict-flex-arrays=0 -fsanitize=array-bounds %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-STRICT-0 +// RUN: %clang_cc1 -O2 -emit-llvm -triple x86_64 -fstrict-flex-arrays=0 -fsanitize=array-bounds -x c++ %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-STRICT-0,CXX,CXX-STRICT-0 +// RUN: %clang_cc1 -O2 -emit-llvm -triple x86_64 -fstrict-flex-arrays=1 -fsanitize=array-bounds %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-STRICT-1 +// RUN: %clang_cc1 -O2 -emit-llvm -triple x86_64 -fstrict-flex-arrays=1 -fsanitize=array-bounds -x c++ %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-STRICT-1,CXX +// RUN: %clang_cc1 -O2 -emit-llvm -triple x86_64 -fstrict-flex-arrays=2 -fsanitize=array-bounds %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-STRICT-2 +// RUN: %clang_cc1 -O2 -emit-llvm -triple x86_64 -fstrict-flex-arrays=2 -fsanitize=array-bounds -x c++ %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-STRICT-2,CXX // Before flexible array member was added to C99, many projects use a // one-element array as the last member of a structure as an alternative. // E.g. https://github.com/python/cpython/issues/84301 // Suppress such errors with -fstrict-flex-arrays=0. + +union Zero { + int a[0]; +}; struct One { int a[1]; }; @@ -19,6 +23,14 @@ int a[3]; }; +// CHECK-LABEL: define {{.*}} @{{.*}}test_zero{{.*}}( +int test_zero(union Zero *p, int i) { + // CHECK-STRICT-0-NOT: @__ubsan + // CHECK-STRICT-1-NOT: @__ubsan + // CHECK-STRICT-2-NOT: @__ubsan + return p->a[i] + (p->a)[i]; +} + // CHECK-LABEL: define {{.*}} @{{.*}}test_one{{.*}}( int test_one(struct One *p, int i) { // CHECK-STRICT-0-NOT: @__ubsan @@ -29,7 +41,15 @@ // CHECK-LABEL: define {{.*}} @{{.*}}test_two{{.*}}( int test_two(struct Two *p, int i) { - // CHECK-STRICT-0: call void @__ubsan_handle_out_of_bounds_abort( + // CHECK-STRICT-0-NOT: @__ubsan + // CHECK-STRICT-1: call void @__ubsan_handle_out_of_bounds_abort( + // CHECK-STRICT-2: call void @__ubsan_handle_out_of_bounds_abort( + return p->a[i] + (p->a)[i]; +} + +// CHECK-LABEL: define {{.*}} @{{.*}}test_three{{.*}}( +int test_three(struct Three *p, int i) { + // CHECK-STRICT-0-NOT: @__ubsan // CHECK-STRICT-1: call void @__ubsan_handle_out_of_bounds_abort( // CHECK-STRICT-2: call void @__ubsan_handle_out_of_bounds_abort( return p->a[i] + (p->a)[i]; @@ -60,29 +80,21 @@ int test_uone(union uOne *p, int i) { // CHECK-STRICT-0-NOT: @__ubsan // CHECK-STRICT-1-NOT: @__ubsan - // CHECK-STRICT-2: @__ubsan + // CHECK-STRICT-2: call void @__ubsan_handle_out_of_bounds_abort( return p->a[i] + (p->a)[i]; } // CHECK-LABEL: define {{.*}} @{{.*}}test_utwo{{.*}}( int test_utwo(union uTwo *p, int i) { - // CHECK-STRICT-0: @__ubsan - // CHECK-STRICT-1: @__ubsan - // CHECK-STRICT-2: @__ubsan + // CHECK-STRICT-0-NOT: @__ubsan + // CHECK-STRICT-1: call void @__ubsan_handle_out_of_bounds_abort( + // CHECK-STRICT-2: call void @__ubsan_handle_out_of_bounds_abort( return p->a[i] + (p->a)[i]; } // CHECK-LABEL: define {{.*}} @{{.*}}test_uthree{{.*}}( int test_uthree(union uThree *p, int i) { - // CHECK-STRICT-0: @__ubsan - // CHECK-STRICT-1: @__ubsan - // CHECK-STRICT-2: @__ubsan - return p->a[i] + (p->a)[i]; -} - -// CHECK-LABEL: define {{.*}} @{{.*}}test_three{{.*}}( -int test_three(struct Three *p, int i) { - // CHECK-STRICT-0: call void @__ubsan_handle_out_of_bounds_abort( + // CHECK-STRICT-0-NOT: @__ubsan // CHECK-STRICT-1: call void @__ubsan_handle_out_of_bounds_abort( // CHECK-STRICT-2: call void @__ubsan_handle_out_of_bounds_abort( return p->a[i] + (p->a)[i]; diff --git a/clang/test/CodeGen/bounds-checking.c b/clang/test/CodeGen/bounds-checking.c --- a/clang/test/CodeGen/bounds-checking.c +++ b/clang/test/CodeGen/bounds-checking.c @@ -1,10 +1,10 @@ // RUN: %clang_cc1 -fsanitize=local-bounds -emit-llvm -triple x86_64-apple-darwin10 %s -o - | FileCheck %s -// RUN: %clang_cc1 -fsanitize=array-bounds -O -fsanitize-trap=array-bounds -emit-llvm -triple x86_64-apple-darwin10 -DNO_DYNAMIC %s -o - | FileCheck %s --check-prefixes=CHECK,NONLOCAL +// RUN: %clang_cc1 -fsanitize=array-bounds -O -fsanitize-trap=array-bounds -emit-llvm -triple x86_64-apple-darwin10 -DNO_DYNAMIC %s -o - | FileCheck %s // // REQUIRES: x86-registered-target -// CHECK-LABEL: @f -double f(int b, int i) { +// CHECK-LABEL: @f1 +double f1(int b, int i) { double a[b]; // CHECK: call {{.*}} @llvm.{{(ubsan)?trap}} return a[i]; @@ -31,29 +31,38 @@ a[2] = 1; } +// CHECK-LABEL: @f4 +__attribute__((no_sanitize("bounds"))) +int f4(int i) { + int b[64]; + // CHECK-NOT: call void @llvm.trap() + // CHECK-NOT: trap: + // CHECK-NOT: cont: + return b[i]; +} + +// Union flexible-array memebers are a C99 extension. All array members with a +// constant size should be considered FAMs. + union U { int a[0]; int b[1]; int c[2]; }; -// CHECK-LABEL: define {{.*}} @f4 -int f4(union U *u, int i) { - // a and b are treated as flexible array members. - // CHECK-NOT: @llvm.ubsantrap - return u->a[i] + u->b[i]; - // CHECK: } -} - -// CHECK-LABEL: define {{.*}} @f5 +// CHECK-LABEL: @f5 int f5(union U *u, int i) { - // c is not a flexible array member. - // NONLOCAL: call {{.*}} @llvm.ubsantrap - return u->c[i]; - // CHECK: } + // a is treated as a flexible array member. + // CHECK-NOT: @llvm.ubsantrap + return u->a[i]; } -__attribute__((no_sanitize("bounds"))) -int f6(int i) { - int b[64]; - // CHECK-NOT: call void @llvm.trap() - // CHECK-NOT: trap: - // CHECK-NOT: cont: - return b[i]; +// CHECK-LABEL: @f6 +int f6(union U *u, int i) { + // b is treated as a flexible array member. + // CHECK-NOT: @llvm.ubsantrap + return u->b[i]; +} + +// CHECK-LABEL: @f7 +int f7(union U *u, int i) { + // c is treated as a flexible array member. + // CHECK-NOT: @llvm.ubsantrap + return u->c[i]; }