I'm working on a small C credential holder called vault, built in WSL. vault_release() is supposed to wipe the secret and then free it:
void vault_release(vault_entry *e)
{
if (!e)
return;
memset(e->secret, 0, e->secret_len);
free(e->secret);
e->secret = NULL;
free(e);
}
The test, tests/wipe_check.c, saves the secret pointer, calls vault_release(), then reads the freed block from byte 16 on. Yes, that's UB. It's a throwaway test and I only want to see what's left in memory. Don't lecture me about it.
Plain make check passes. make release-check does a clean and then make check OPT=-O2, and that one fails. The bytes after the first 16 are still the original secret, and only the first 16 look clobbered. I'm skipping those 16 in the test anyway, since glibc puts its own bookkeeping there.
The memset is right there on the whole length, and I've read this function three times. My plain target should already be optimizing, since that's the default, so I don't see how the same source gives two results. Either glibc's free() writes stuff back into the block, or my -O2 target builds something different from the plain one. Neither explanation really makes sense to me.
I haven't looked at the generated assembly yet. What am I missing here?