2
// Compiled with GCC 7.3.0, x86-64 -g -O3 -std=gnu++11
// Tested on OS (1) Ubuntu 18.04 LTS, (2) Gentoo

int Listen(... socket)
{
    char buffer[INT16_MAX];
    . . .
    . . . = recvfrom(socket, buffer, ....)
    ParseMsg(buffer)
}

void ParseMsg(uint8_t *const msg)
{
    . . .
    uint16_t* word_arr = (uint16_t*)(msg+15); // if I changed 15 to 16 (aligned to uint16_t) 
                                              // the program doesn't crash

    for(size_t i = 0 ; i < 30 ; ++i)
    {
        word_arr[i] = 1; // after some iterations (around 13) the program crashes with segmentation fault
                         // if i add a print statement inside the loop the program doesn't crash
    }

    // word_arr[20] = 1; // if I assign value not in loop the program doesn't crash 
}

I found some links that talk about this matter:
https://github.com/samtools/htslib/issues/400
https://github.com/xianyi/OpenBLAS/issues/1137
but they talk in processor terminology. Can someone confirm that this bug exists ?

p.s
I ran the code with -O2 optimization flag. Didn't crash

Alex B
  • 43
  • 5
  • 1
    You should look in a debugger at the instruction that fails. Most likely a vector instruction. It doesn't matter if the hardware usually accepts unaligned reads of that size, if the C++ standard says that unaligned reads are illegal, the compiler can use that information to optimize. – Marc Glisse May 25 '21 at 15:31
  • 1
    This is not a bug in the optimizer it is a problem in your code. According to the C++ standard unaligned access have undefined behaviour. – Johan May 26 '21 at 07:16
  • Your code is ill-formed due to the strict aliasing rule. See the recent question https://stackoverflow.com/a/67636420/12939557 and the famous one https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule. – Jérôme Richard May 26 '21 at 17:22
  • 1
    @JérômeRichard Here it gets a little complicated. Char pointers (including `signed char` and `unsigned char`) are explicitly excluded from the strict aliasing rule and on most implementations `uint8_t` is a typdef from `unsigned char` and thus the strict aliasing rule does not apply. – Johan May 27 '21 at 06:48
  • @Johan Indeed! I was confused with the type `uint8_t`. My bad. Anyway, the solution to fix this problem looks like the same as for type punning issues: use `memcpy` (or `bitcast` in C++20, or also unions in C). – Jérôme Richard May 27 '21 at 14:48

1 Answers1

3

The problem does underline in the alignment of the uint16_t pointer (which should be 2), in combination with -O3 the compiler is trying to optimize your loop using vectorization (SIMD), hence trying to load unaligned argument to SSE registers.

This can explain why it works when:

  1. you change the offset from 15 to 16 - causes the uint16_t pointer to be aligned
  2. with -O2 - disable the vectorization
  3. removing the loop / adding a print within the loop - also disable the vectorization

Please refer to the following link with a question similar to yours and some great elaborate answers:

c-undefined-behavior-strict-aliasing-rule-or-incorrect-alignment

Cool_Br33ze
  • 46
  • 1
  • 3
  • vectorization optimization does seems to be the problem! compiling with -03 and -fno-tree-vectorize (that disables the vectorization) caused the program NOT to crash – Alex B May 27 '21 at 13:27