Search code examples
ubsan

UBSan: Store to misaligned address; what is the problem, and should I care


I've been running some code under UBSan, and found an error which I've never seen before:

/usr/include/c++/7/bits/stl_algobase.h:324:8: runtime error: store to misaligned address 0x611000001383 for type 'struct complex', which requires 4 byte alignment
0x611000001383: note: pointer points here
 66  46 40 02 00 00 00 00 00  00 00 00 04 01 18 00 08  00 00 00 00 00 00 00 08  00 00 00 00 00 00 00
              ^

(g++-7.3.0, Ubuntu 18.04, flags -fsanitize=address -fsanitize=undefined)

What does this error mean? Is it truly an error (it is in the standard library, so it can't be too bad, right?), and should I care about it?


Solution

  • You probably use a pointer cast which casts a block of raw memory to a complex*.

    Example:

    void* raw = getBuffer(); // Made up function which returns a buffer
    auto size = *static_cast<uint16_t>*(raw); // Maybe your format says that you got a 2 Byte size in front
    auto* array = static_cast<complex*>(raw+sizeof(uint16_t)); // ... and complex numbers after
    std::transform(array, array+size, ...);  // Pass this into STL
    

    Boom! You got UB.

    Why?

    The behavior is undefined in the following circumstances: [...]
    Conversion between two pointer types produces a result that is incorrectly aligned

    [...]

    If the resulting pointer is not correctly aligned [68] for the referenced type, the behavior is undefined.

    See https://stackoverflow.com/a/46790815/1930508 (where I got these from)

    What does it mean?
    Every pointer must be aligned to the type it is pointing to. For complex this means an alignment of 4. In short this means that array (from above) must be evenly divisible by 4 (aka array % 4 == 0) Assuming that raw is aligned to 4 bytes you can easily see that array cannot as (raw + 2) % 4 == 2 (because of raw % 4 == 2)
    If the size would be a 4-Byte value, then array would have been aligned if (and only if) raw was aligned. Whether this is guaranteed depends on where it comes from.

    So yes this is truly an error and may lead to a real bug although not always (depending on moon phase etc. as it is always with UB, see the answer above for details)

    And no it is NOT in the STL, it just happens to be detected there because UBSAN watches memory dereferences. So while the actual UB is the static_cast<complex*> it is only detected when reading from that pointer.

    You can use export UBSAN_OPTIONS=print_stacktrace=1 prior to executing the program to get a stacktrace and find out where your wrong cast is.

    Tip: You only need to check casts. Any struct/type allocated via new is always aligned (and every member inside), unless tricks like "packed structs" are used.