#344

Reject global and TLS initializers that store pointers at misaligned offsets

Openmbrockwants to merge intofrom

Work mostly by Opus 5.5.

I also put a more friendly write-up with a video introduction on https://mbrock.github.io/filnix/misaligned-initializers/ which may be of interest to someone.


A packed record (or an array of packed records/unions whose stride is not a multiple of 8) can put a pointer at a non-word offset in a global or thread-local initializer. Such a pointer has no shadow word of its own, and initializers are stored without the alignent checks that guard ordinary pointer loads and stores.

int anchor = 5;
struct __attribute__((packed)) P { char c; int *p; };
_Thread_local struct P t = { 1, &anchor };   /* or a plain global */

On 0.686 this does two different bad things:

  • Global: computeConstantRelocations asserts !(Offset % WordSize) and clang aborts (exit 134) instead of diagnosing the program.
  • Thread-local: the getter stores the initializer with storeValueRecurseAfterCheck, which writes the capability at aux + 1, across two shadow words, under a false align 8. Shadow word 0 then holds the capability shifted by a byte — non-null, but not a capability. A later ordinary aligned pointer load of word 0 (where the initializer never stored a pointer) reads that garbage as a capability and the program segfaults with no Fil-C diagnostic.

This patch makes both cases a compile error:

error: Fil-C cannot initialize 't': a pointer in its initializer is not word-aligned