https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104607

            Bug ID: 104607
           Summary: Struct padding not initialized when all members are
                    provided initializers
           Product: gcc
           Version: 12.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: pascal_cuoq at hotmail dot com
  Target Milestone: ---

In the GCC versions available as 11.2 and as “trunk” (as of this writing) on
Compiler Explorer, the following C code is translated to the following X86-64
assembly, with options “-O2 -std=c17”

CE link: https://gcc.godbolt.org/z/4oeh6d7vY

void g(void*);

int f(void) {
  struct s { char c; long long m64;} s12 = {1,2}, s1 = {1};
  g(&s1);
  g(&s12);
}

f:
        subq    $40, %rsp
        pxor    %xmm0, %xmm0
        leaq    16(%rsp), %rdi
        movaps  %xmm0, 16(%rsp)
        movb    $1, (%rsp)
        movq    $2, 8(%rsp)
        movb    $1, 16(%rsp)
        call    g
        movq    %rsp, %rdi
        call    g
        addq    $40, %rsp
        ret

Please note that GCC initializes all of s1 with “movaps  %xmm0, 16(%rsp)” and
that initializing only s1.m64 to 0 in addition to initializing s1.c to 1 would
have resulted in fewer, shorter instructions.

However, s12 is not initialized this way, and indeed the padding of s12 is not
initialized at all.

The requirement to initialize padding is a change that was introduced between
C99 and C11. The relevant clauses are in C99:

https://port70.net/~nsz/c/c99/n1256.html#6.7.8p10

If an object that has automatic storage duration is not initialized explicitly,
its value is indeterminate. If an object that has static storage duration is
not initialized explicitly, then:

if it has pointer type, it is initialized to a null pointer;
if it has arithmetic type, it is initialized to (positive or unsigned) zero;
if it is an aggregate, every member is initialized (recursively) according to
these rules;
if it is a union, the first named member is initialized (recursively) according
to these rules.

https://port70.net/~nsz/c/c99/n1256.html#6.7.8p19

The initialization shall occur in initializer list order, each initializer
provided for a particular subobject overriding any previously listed
initializer for the same subobject;132) all subobjects that are not initialized
explicitly shall be initialized implicitly the same as objects that have static
storage duration.

In C11 the clause that was numbered 6.7.8:10 in C99 becomes 6.7.9:10 and the
text is changed as follows:

If an object that has automatic storage duration is not initialized explicitly,
its value is indeterminate. If an object that has static or thread storage
duration is not initialized explicitly, then:

if it has pointer type, it is initialized to a null pointer;
if it has arithmetic type, it is initialized to (positive or unsigned) zero;
if it is an aggregate, every member is initialized (recursively) according to
these rules, and any padding is initialized to zero bits;
if it is a union, the first named member is initialized (recursively) according
to these rules, and any padding is initialized to zero bits;

Now, I agree that it's possible to read the change in a way that only forces
the initialization of partially initialized structs. But can this have been the
intention of the standardization committee when they went to the trouble of
making an explicit change to C11? The concern was likely to address some
security issues with information leaks. If structs with initializers for every
members do not have their padding initialized, then security issues with
information leaks remain for structs with initializers for every members. Why
would the committee have fixed only half of the problem?

Reply via email to