Search code examples
c++initializationg++clang++zero-initialization

Using `-ftrivial-auto-var-init` to guarantee the initialization of padding bytes to zero for non-static aggregate objects


I'm following Does C++ standard guarantee the initialization of padding bytes to zero for non-static aggregate objects? and trying to force GCC and Clang to initialize padding with zero. The -ftrivial-auto-var-init=choice flag seems to do the trick, but I don't see any difference between using it or not

The original code is here (without using -ftrivial-auto-var-init=zero), and produces non-zeroed members:

GCC

Foo():
x:[----][0x42][0x43][0x44],v: 0
y:[----][----][----][----],v: 0
z:[----][0x4A][0x4B][0x4C],v: 0

Foo{}:
x:[----][----][----][----],v: 0
y:[----][----][----][----],v: 0
z:[----][----][----][----],v: 0

Clang

Foo():
x:[----][----][----][----],v: 0
y:[----][----][----][----],v: 0
z:[----][----][----][----],v: 0

Foo{}:
x:[----][0x42][0x43][0x44],v: 0
y:[----][----][----][----],v: 0
z:[----][0x4A][0x4B][0x4C],v: 0

I've added the -ftrivial-auto-var-init=zero flag to both GCC 12 and Clang 15. but the results are exactly the same, so I'm a bit confused as to what -ftrivial-auto-var-init=zero should do.

GCC

Foo():
x:[----][0x42][0x43][0x44],v: 0
y:[----][----][----][----],v: 0
z:[----][0x4A][0x4B][0x4C],v: 0

Foo{}:
x:[----][----][----][----],v: 0
y:[----][----][----][----],v: 0
z:[----][----][----][----],v: 0

Clang

Foo():
x:[----][----][----][----],v: 0
y:[----][----][----][----],v: 0
z:[----][----][----][----],v: 0

Foo{}:
x:[----][0x42][0x43][0x44],v: 0
y:[----][----][----][----],v: 0
z:[----][0x4A][0x4B][0x4C],v: 0

Solution

  • Note that -ftrivial-auto-var-init only applies to automatic variables (like the name suggests):

    Initialize automatic variables with either a pattern or with zeroes to increase the security and predictability of a program by preventing uninitialized memory disclosure and use.

    Automatic variables are variables that have automatic storage duration, i.e. local block-scope variables only.

    Note that if you're using new / delete then your objects will have dynamic storage duration and therefore -ftrivial-auto-var-init will not apply to them.


    Example

    This example demonstrates the effect of -ftrivial-auto-var-init=zero:
    godbolt

    struct Foo
    {
        char x;
        int y;
        char z;
    };
    
    int main()
    {
        {
            char buf[sizeof(Foo)];
            for(int i = 0; i < sizeof(buf); ++i)
            {
                buf[i] = 'A' + i;
            }
            buf[sizeof(Foo)-1] = '\0';
            printf("%s\n", buf);
        }
        {
            Foo f;
            DumpF("Foo, automatic", &f);
        }
    }
    

    When compiled with -ftrivial-auto-var-init=zero you're guaranteed to get:

    Foo, automatic:
    x:[----][----][----][----],v: 0
    y:[----][----][----][----],v: 0
    z:[----][----][----][----],v: 0
    

    Whereas if you compile without it you'll most likely get random bytes from the stack (in this case gcc chose to reuse the space of buf):

    Foo, automatic:
    x:[0x41][0x42][0x43][0x44],v: 65
    y:[0x45][0x46][0x47][0x48],v: 1212630597
    z:[0x49][0x4A][0x4B][----],v: 73