https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128359

            Bug ID: 128359
           Summary: Misaligned vmovdqa64 store to ASan fake stack frame
                    (detect_stack_use_after_return) with
                    -march=sapphirerapids
           Product: gcc
           Version: 13.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: bingz at nvidia dot com
  Target Milestone: ---

With -fsanitize=address on an AVX-512 target, GCC (-O1 and up) emits
   64-byte aligned stores (vmovdqa64 %zmm) to locals in an ASan frame whose
   base is only 32-byte aligned relative to those locals. On the real stack
   it works, because the prologue realigns %rsp so the frame lines up. When
   libasan puts the frame on the fake stack (detect_stack_use_after_return=1,
   the default in libasan8 14.2), __asan_stack_malloc_7 returns a page-aligned
   base. The store then lands 32 bytes off a 64-byte boundary, raises #GP,
   and ASan reports "SEGV on unknown address ... high value address".

   This is the same kind of problem as PR110027 and PR120201. The store here
   is an aggregate zero-initializer, not a memcpy.
   >>> Also reproduced with gcc <13.5 / 16.2 / trunk>, which has the PR120201
fix (r17-2387).

   Versions / environment:
     gcc 13.3.0 (Ubuntu 13.3.0-6ubuntu2~24.04)    -O1/-O2/-O3 bad, -O0 ok
     gcc 14.2.0 (Ubuntu 14.2.0-4ubuntu2~24.04.1)  -O1/-O2/-O3 bad, -O0 ok
     clang 20.1.2 with the same flags: ok
     Ubuntu 24.04, x86_64-linux-gnu, Intel Xeon Platinum 8580 (Sapphire Rapids)
     [paste `gcc -v` output here]

   Reproducer (attached: mlx5_flow_dv.i.xz = preprocessed
   drivers/net/mlx5/mlx5_flow_dv.c from DPDK 26.11-rc1; check_misalign.py):

     xz -d mlx5_flow_dv.i.xz
     gcc -w -O2 -march=sapphirerapids -mrtm -fPIC -fsanitize=address \
         -c mlx5_flow_dv.i -o t.o
     python3 check_misalign.py t.o     # prints offsets, exit status 1 = bug
present

   (The TU needs -mrtm. -march=x86-64-v4 doesn't compile it because it uses
   AVX512IFMA intrinsics. check_misalign.py disassembles flow_dv_translate and
   works out each aligned zmm access's offset mod 64 for both stacks.)

   The affected function is flow_dv_translate(). The first faulting store at
   run time is the zero-init of

     void *sample_actions[8] = {0};      /* line 15106, 64 bytes, natural align
8 */

   Other affected locals in the same function: line 14881
   (struct mlx5_flow_dv_encap_decap_resource res = {...}, -O1/-O2) and
   line 5155 (struct mlx5_dv_matcher_workspace wks = {...}, -O3).

   gcc 13.3 -O3 code:

     flow_dv_translate:
       lea    0x8(%rsp),%r10
       and    $0xffffffffffffffc0,%rsp       ; realign to 64
       push   -0x8(%r10)
       push   %rbp
       mov    %rsp,%rbp                      ; rbp == 0x30 (mod 64)
       ...
       lea    -0x18d0(%rbp),%rax             ; real-stack frame base, == 0x20
(mod 64)
       mov    %rax,-0x1930(%rbp)
       mov    __asan_option_detect_stack_use_after_return,%rax
       mov    (%rax),%ebx
       test   %ebx,%ebx
       jne    <fake>
       ...
     <fake>:
       mov    $0x1880,%edi
       call   __asan_stack_malloc_7          ; page-aligned base
       test   %rax,%rax
       cmove  -0x1930(%rbp),%rax
       mov    %rax,-0x1930(%rbp)
       ...
       lea    0x18a0(%rax),%rbx              ; rbx = base + 0x18a0
       movq   $0x41b58ab3,(%rax)             ; ASan frame magic at base+0
       ...
       vmovdqa64 %zmm0,-0xa40(%rbx)          ; line 15106: base+0xe60  <--
faults

     real stack: base == 0x20 (mod 64) -> base+0xe60 == 0  (mod 64)  ok
     fake stack: base == 0    (mod 64) -> base+0xe60 == 32 (mod 64)  #GP

   So the frame layout assumes base == 32 (mod 64), but the fake-stack base
   is 0 (mod 64).

   All aligned zmm accesses through the frame register (check_misalign.py),
   each 32 (mod 64) on the fake stack and 0 (mod 64) on the real stack:

     gcc-13 -O1: base+0xc60, +0xd20, +0xd20
     gcc-13 -O2: base+0xe60, +0xf20
     gcc-13 -O3: base+0xe60, +0xfa0, +0xf20, +0xf60
     gcc-14 -O1: base+0xd20, +0xde0, +0xde0
     gcc-14 -O2: base+0xe60, +0xf20
     gcc-14 -O3: base+0xe60, +0xfa0, +0xf20, +0xf60
     -O0 (both): no 64-byte realignment and no aligned zmm accesses

   For comparison, other functions in the same DPDK build that have 64-byte
   aligned locals (e.g. mlx5dr_send_wqe_fw, mlx5dr_rule_create_hws,
   __mlx5_hws_cnt_svc) get a correct layout: the real frame base is 64-byte
   aligned and every aligned zmm access is aligned on both stacks. My guess
   is that in flow_dv_translate the 64-byte requirement comes from the
   optimizer/expander (vectorized zero-init), not from the declared alignment
   of the variables, so the PR110027 fix doesn't cover it.

   Run-time failure (DPDK dpdk-testpmd with mlx5 NICs, gcc -O2):

     AddressSanitizer:DEADLYSIGNAL
     ==155456==ERROR: AddressSanitizer: SEGV on unknown address (pc
0x5934bad309c4 bp 0x7ffeeeaa8830 sp 0x7ffeeeaa7000 T0)
     ==155456==The signal is caused by a READ memory access.
     ==155456==Hint: this fault was caused by a dereference of a high value
address
         #0 flow_dv_translate ../drivers/net/mlx5/mlx5_flow_dv.c:15106
         #1 flow_create_split_metadata ../drivers/net/mlx5/mlx5_flow.c:6651
         ...
     SUMMARY: AddressSanitizer: SEGV ../drivers/net/mlx5/mlx5_flow_dv.c:15106
in flow_dv_translate

   The same program runs cleanly when built with gcc -O0 or with clang -O3.
   __attribute__((no_sanitize_address)) on flow_dv_translate also avoids it
   (the zmm stores become vmovdqu8).

// The details are summarized and polished by the AI.

Reply via email to