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

            Bug ID: 101419
           Summary: collapsing memset() calls can break
                    __builtin_object_size()
           Product: gcc
           Version: unknown
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: kees at outflux dot net
  Target Milestone: ---

Created attachment 51131
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=51131&action=edit
memset collapsing breaks __builtin_object_size()

I've found a strange misoptimization around memset() and
__builtin_object_size(). If there are two memset() calls that can be collapsed
(due to being fully overlapping, I assume), use of __builtin_object_size() may
return the wrong result.

The example code shows that __builtin_object_size(&int_value, 1) returns 1
instead of 4:

> $ gcc  -Wall -Wextra -fno-strict-aliasing -fwrapv -O2 -c -o wat.o wat.c 
> In function ‘do_wipe’,
>     inlined from ‘loops’ at wat.c:24:3:
> wat.c:15:3: warning: call to ‘__detected_overflow’ declared with attribute 
> warning: detected overflow [-Wattribute-warning]
>    15 |   __detected_overflow(__builtin_object_size(&info->lg, 1), 
> sizeof(info->lg));
>       |   
> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Here, info->lg is int, but the call to __builtin_object_size() resolves to the
size of info->sm (char). This can be seen directly in the resulting output:

> $ objdump -rd wat.o
> ...
> 0000000000000000 <loops>:
>    0:   53                      push   %rbx
>    1:   be 04 00 00 00          mov    $0x4,%esi
>    6:   48 89 fb                mov    %rdi,%rbx
>    9:   c6 07 00                movb   $0x0,(%rdi)
>    c:   bf 01 00 00 00          mov    $0x1,%edi
>   11:   e8 00 00 00 00          call   16 <loops+0x16>
>                         12: R_X86_64_PLT32      __detected_overflow-0x4
>   16:   c7 03 00 00 00 00       movl   $0x0,(%rbx)
>   1c:   5b                      pop    %rbx
>   1d:   c3                      ret    

The first argument to __detected_overflow() is "1", instead of 4.

Any changes to this example code makes the bug disappear (removal of loops,
removal of empty asm, or reordering of memset() calls).

Using Compiler Explorer, this bug appears to have been introduced between GCC
8.5 and 9.1: https://godbolt.org/z/oGq5K9fE4

Reply via email to