-fsanitize=undefined and alignment on x86_64

Hi all,

sanitize=undefined is giving me an error message about unaligned access to a size_t. It’s in a packed structure, and indeed is not 8 byte aligned (although it is 4 byte aligned.) This is in Linux on an x86_64 architecture.

My question is: why is this undefined behavior? Can’t modern Intel and AMD processors accesses unaligned ints, although with a possible performance penalty? So this would be a performance problem, but not correctness? Is the problem that the compiler could use SSE or some other instructions that require alignment and will seg fault or give wrong results with unaligned access?

Best,
Martin

Hi all,

sanitize=undefined is giving me an error message about unaligned access to a size_t. It’s in a packed structure, and indeed is not 8 byte aligned (although it is 4 byte aligned.) This is in Linux on an x86_64 architecture.

My question is: why is this undefined behavior?

In short: because the C++ standard says so. -fsanitize=undefined is designed to catch programs that have undefined behavior according to the C++ standard.

Can’t modern Intel and AMD processors accesses unaligned ints, although with a possible performance penalty? So this would be a performance problem, but not correctness?

Not necessarily. The compiler is still allowed to optimize on the basis that the program doesn’t do this. Even beyond ignoring the x86 cost penalty for unaligned stores - it might optimize out tests on the low bits of a pointer, knowing they must be zero for example.

I’m not sure if LLVM has any particular optimizations in this area at the moment.

sanitize=undefined is giving me an error message about unaligned access to
a size_t. It's in a packed structure, and indeed is not 8 byte aligned
(although it is 4 byte aligned.)

In short: because the C++ standard says so. -fsanitize=undefined is designed
to catch programs that have undefined behavior according to the C++
standard.

Surely the standard doesn't say anything about packed structures? If
Clang supports them I'd expect accesses to be lowered to ones which
the target supports (based on the DataLayout, perhaps amongst other
things).

Tim.

The Standard simply says that access should be aligned at least to the natural alignment of the type. You are free to overalign but not to underalign.

Various compilers provide extensions to “pack” structures and a number of CPUs support unaligned accesses, but the Standard is concerned about ALL architectures and thus Standard compliant code should conform to alignment.

If possible you might be able to either:

  • deactivate the check that is not applicable to your situation
  • blacklist the functions/files that perform that check (doing so will remove all checks in the concerned functions/files though as far as I know)

– Matthieu

Please can you provide a code sample which triggers the issue? Packed
structs are supported by -fsanitize=alignment. However, note that
taking the address of a misaligned member of a packed struct will not
in general work:

struct __attribute__((packed)) S {
  char c;
  long l;
} s;
int f() { return s.l; } // ok
int g() { return *&s.l; } // undefined behavior: long* access requires
8 byte alignment

The Standard simply says that access should be aligned at least to the
natural alignment of the type. You are free to overalign but not to
underalign.

Various compilers provide extensions to "pack" structures and a number of
CPUs support unaligned accesses, but the Standard is concerned about ALL
architectures and thus Standard compliant code should conform to alignment.

Well, if you were writing standard compliant code you wouldn't be
using packed structs in the first place. I suppose the question I'd
have is how Clang intends packed structs to fit in.

Saying that the programmer can use them but their code immediately
becomes undefined behaviour by doing so doesn't seem tenable to me.

Some kind of type qualifier seems to be the obvious candidate: if
"__attribute__((packed)) size_t" has no alignment requirements then
most obvious properties follow (you can access fields directly, but
not take pointers). But there may be issues with overloads and other
things I've only vaguely heard about.

Does anyone know what our official intent is, out of curiosity?

Tim.

This is a GCC extension; our intent is to match GCC. That is:

Direct access to, and modification of, fields of packed structs is OK.
Taking addresses of them yields a pointer which you can't use for any
operations which require more aligment than the pointer actually has.

This is essentially what I was doing.

So that’s potentially bad even on x86_64, because optimizations are free to assume the lower order bits of the pointer are zero?

This is essentially what I was doing.

So that's potentially bad even on x86_64, because optimizations are free to
assume the lower order bits of the pointer are zero?

Yes, exactly.