Bug 69696 - incorrect initialization of block-scope flexible array members
Summary: incorrect initialization of block-scope flexible array members
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 6.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: wrong-code
Depends on:
Blocks: flexmembers
  Show dependency treegraph
 
Reported: 2016-02-05 19:23 UTC by Martin Sebor
Modified: 2019-01-07 17:36 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail: 4.9.3, 5.3.0, 6.0
Last reconfirmed: 2017-08-29 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Martin Sebor 2016-02-05 19:23:04 UTC
As pointed in the following discussion https://gcc.gnu.org/ml/gcc-patches/2016-02/msg00420.html, g++ allows local structs with flexible array members to be initialized on definition but then allocates the arrays in a way that overlaps other local variables, leading to data corruption.  In C mode, GCC rejects initialization of such structs (this was done to fix bug 28865).

G++ should either also reject initialiation of such structs, or it should do it without this overlapping problem.  Note that bug 68489 is also related to this problem, as is bug 69338.

The following test case shows that while structures containing flexible array members and having static or thread storage duration are initialized correctly, those having automatic storage duration are not. 

$ cat t.c && /build/gcc-trunk/gcc/xgcc -B /build/gcc-trunk/gcc -Wall -Wextra -Wpedantic -xc++ t.c && ./a.out
enum { X = 12345678, Y = 87654321 };

struct A { int n, a []; };
struct A a0 = { 1, { 2, 3, 4 } };

void print (const int *i, struct A *a, const int *j)
{
    __builtin_printf ("\ni = %i\n"
                      "a = { %i, { %i, %i, %i }\n"
                      "j = %i\n",
                      *i, a->n, a->a[0], a->a[1], a->a[2],
                      *j);

    if (*i != X || *j != Y || a->a[0] != 2 || a->a[1] != 3 || a->a[2] != 4)
       __builtin_abort ();
 }

int main ()
{
    {
        static int i = X;
        static struct A a = { 1, { 2, 3, 4 } };
        static int j = Y;

        print (&i, &a, &j);
    }
    {
        __thread int i = X;
        __thread struct A a = { 1, { 2, 3, 4 } };
        __thread int j = Y;

        print (&i, &a, &j);
    }
    {
        int i = X;
        struct A a = { 1, { 2, 3, 4 } };
        int j = Y;

        print (&i, &a, &j);
    }
}
t.c:4:32: warning: initialization of a flexible array member [-Wpedantic]
 struct A a0 = { 1, { 2, 3, 4 } };
                                ^
t.c: In function ‘int main()’:
t.c:22:46: warning: initialization of a flexible array member [-Wpedantic]
         static struct A a = { 1, { 2, 3, 4 } };
                                              ^
t.c:28:22: warning: function-scope ‘i’ implicitly auto and declared ‘__thread’
         __thread int i = X;
                      ^
t.c:29:27: warning: function-scope ‘a’ implicitly auto and declared ‘__thread’
         __thread struct A a = { 1, { 2, 3, 4 } };
                           ^
t.c:29:48: warning: initialization of a flexible array member [-Wpedantic]
         __thread struct A a = { 1, { 2, 3, 4 } };
                                                ^
t.c:30:22: warning: function-scope ‘j’ implicitly auto and declared ‘__thread’
         __thread int j = Y;
                      ^
t.c:36:39: warning: initialization of a flexible array member [-Wpedantic]
         struct A a = { 1, { 2, 3, 4 } };
                                       ^

i = 12345678
a = { 1, { 2, 3, 4 }
j = 87654321

i = 12345678
a = { 1, { 2, 3, 4 }
j = 87654321

i = 4
a = { 1, { 2, 3, 4 }
j = 87654321
Aborted (core dumped)
Comment 1 Martin Sebor 2016-02-05 19:24:11 UTC
This problem seems to have always existed in C++.
Comment 2 Eric Gallager 2017-08-29 01:32:50 UTC
I get slightly different output, but same basic problem:

$ /usr/local/bin/g++ -Wall -Wextra -Wpedantic -xc++ -o 69696.exe 69696.c
69696.c:3:22: warning: ISO C++ forbids flexible array member ‘a’ [-Wpedantic]
 struct A { int n, a []; };
                      ^
69696.c:4:32: warning: initialization of a flexible array member [-Wpedantic]
 struct A a0 = { 1, { 2, 3, 4 } };
                                ^
69696.c: In function ‘int main()’:
69696.c:22:46: warning: initialization of a flexible array member [-Wpedantic]
         static struct A a = { 1, { 2, 3, 4 } };
                                              ^
69696.c:28:22: warning: function-scope ‘i’ implicitly auto and declared ‘__thread’
         __thread int i = X;
                      ^
69696.c:29:27: warning: function-scope ‘a’ implicitly auto and declared ‘__thread’
         __thread struct A a = { 1, { 2, 3, 4 } };
                           ^
69696.c:29:48: warning: initialization of a flexible array member [-Wpedantic]
         __thread struct A a = { 1, { 2, 3, 4 } };
                                                ^
69696.c:30:22: warning: function-scope ‘j’ implicitly auto and declared ‘__thread’
         __thread int j = Y;
                      ^
69696.c:36:39: warning: initialization of a flexible array member [-Wpedantic]
         struct A a = { 1, { 2, 3, 4 } };
                                       ^
$ ./69696.exe

i = 12345678
a = { 1, { 2, 3, 4 }
j = 87654321

i = 12345678
a = { 1, { 0, 0, 4194928 }
j = 87654321
Abort trap
$

Also, as a separate issue, the "implicitly auto and declared __thread" warning should have its own separate flag controlling it.
Comment 3 Bernd Edlinger 2019-01-07 17:09:24 UTC
Author: edlinger
Date: Mon Jan  7 17:08:51 2019
New Revision: 267653

URL: https://gcc.gnu.org/viewcvs?rev=267653&root=gcc&view=rev
Log:
        PR c++/88261
        PR c++/69338
        PR c++/69696
        PR c++/69697
        * cp-tree.h (LOOKUP_ALLOW_FLEXARRAY_INIT): New flag value.
        * typeck2.c (digest_init_r): Raise an error for non-static
        initialization of a flexible array member.
        (process_init_constructor, massage_init_elt,
        process_init_constructor_array, process_init_constructor_record,
        process_init_constructor_union, process_init_constructor): Add the
        flags parameter and pass it thru.
        (store_init_value): Pass LOOKUP_ALLOW_FLEXARRAY_INIT parameter to
        digest_init_flags for static decls.

gcc/testsuite:
2019-01-07  Bernd Edlinger  <bernd.edlinger@hotmail.de>

        PR c++/88261
        PR c++/69338
        PR c++/69696
        PR c++/69697
        * gcc.dg/array-6.c: Move from here ...
        * c-c++-common/array-6.c: ... to here and add some more test coverage.
        * g++.dg/pr69338.C: New test.
        * g++.dg/pr69697.C: Likewise.
        * g++.dg/ext/flexary32.C: Likewise.
        * g++.dg/ext/flexary3.C: Adjust test.
        * g++.dg/ext/flexary12.C: Likewise.
        * g++.dg/ext/flexary13.C: Likewise.
        * g++.dg/ext/flexary15.C: Likewise.
        * g++.dg/warn/Wplacement-new-size-1.C: Likewise.
        * g++.dg/warn/Wplacement-new-size-2.C: Likewise.
        * g++.dg/warn/Wplacement-new-size-6.C: Likewise.

Added:
    trunk/gcc/testsuite/c-c++-common/array-6.c
      - copied, changed from r267652, trunk/gcc/testsuite/gcc.dg/array-6.c
    trunk/gcc/testsuite/g++.dg/ext/flexary32.C
    trunk/gcc/testsuite/g++.dg/pr69338.C
    trunk/gcc/testsuite/g++.dg/pr69697.C
Removed:
    trunk/gcc/testsuite/gcc.dg/array-6.c
Modified:
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/cp-tree.h
    trunk/gcc/cp/typeck2.c
    trunk/gcc/testsuite/ChangeLog
    trunk/gcc/testsuite/g++.dg/ext/flexary12.C
    trunk/gcc/testsuite/g++.dg/ext/flexary13.C
    trunk/gcc/testsuite/g++.dg/ext/flexary15.C
    trunk/gcc/testsuite/g++.dg/ext/flexary3.C
    trunk/gcc/testsuite/g++.dg/warn/Wplacement-new-size-1.C
    trunk/gcc/testsuite/g++.dg/warn/Wplacement-new-size-2.C
    trunk/gcc/testsuite/g++.dg/warn/Wplacement-new-size-6.C
Comment 4 Bernd Edlinger 2019-01-07 17:36:20 UTC
Fixed on trunk.