Whilst developing Privilege Separation in dhcpcd, I had to come up with an IPC design for it. Of course, that involves creating structures.
So far, my structures in dhcpcd are long lived- or rather the scope is design to live outside of where it was created. As such they are created on the heap and are at the mercy of malloc. Generally I use calloc so that the whole area is inited to zero as uninitialised memory is bad.
So I decided to start out and see if I can just create the structures I need on the stack. Turns out I could! Yay! Now, how to you initialise a structure on the stack to all zeros? First let us consider this structure:
struct ps_addr {
sa_family_t psa_family;
union {
struct in_addr psau_in_addr;
struct in6_addr psau_in6_addr;
} psa_u;
#define psa_in_addr psa_u.psau_in_addr
#define psa_in6_addr psa_u.psau_in6_addr
};
The first way is memset:
struct ps_addr psa;
memset(&psa, 0, sizeof(psa));
psa.psa_family = AF_INET;
But what if you could avoid memset? Luckily the C standard allows setting any member and will zero all other members. So we can do this:
struct ps_addr psa = { .psa_family = AF_INET };
Wow!!! So simple. This reduces binary size a fair bit. But then I turned on the Memory Sanitiser and boom, it crashed hard. Why?
The answer is simple- padding. Eric S Raymond gives a very good writeup about the problem. Basically, the standard will initialise any unintialised members to zero- but padding added for alignent isn’t a member! So we need to ensure that our structure requires zero padding.
Here is the new struct:
struct ps_addr {
sa_family_t psa_family;
uint8_t psa_pad[4- sizeof(sa_family_t)];
union {
struct in_addr psau_in_addr;
struct in6_addr psau_in6_addr;
} psa_u;
#define psa_in_addr psa_u.psau_in_addr
#define psa_in6_addr psa_u.psau_in6_addr
};
And it allows the former structure initialisation to work and memory sanitisers are happy- so happy days :) Now, if anyone can tell me what I can use instead of the magic number 4 in the above I’d be even happier!