Bug 96259

Summary: [10/11 Regression] warning on GCC 10
Product: gcc Reporter: Frediano Ziglio <freddy77>
Component: sanitizerAssignee: Not yet assigned to anyone <unassigned>
Status: RESOLVED DUPLICATE    
Severity: normal CC: dodji, dvyukov, jakub, kcc, marxin, msebor
Priority: P3 Keywords: diagnostic
Version: 10.1.0   
Target Milestone: 10.2   
See Also: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95988
Host: Target:
Build: Known to work:
Known to fail: Last reconfirmed:

Description Frediano Ziglio 2020-07-21 09:26:27 UTC
I found this issue updating a software from Fedora 31 to Fedora 32. I manage to reproduce the problem with this main.c file:


struct DataIn {
   int type;
   int dummy;
};

struct DataOut {
   struct DataIn in;
};

void func(struct DataOut *data)
{
   if (data->in.type) {
       __builtin_memset(&(data->in), 0, sizeof(struct DataIn));
   }
}


and using this command line:

   gcc -Warray-bounds=2 -Werror -O2 -fsanitize=address -c main.c

this is reporting:


<source>: In function 'void func(DataOut*)':
<source>:15:24: error: 'void* __builtin_memset(void*, int, long unsigned int)' offset [4, 7] from the object at 'data' is out of the bounds of referenced subobject 'DataIn::type' with type 'int' at offset 0 [-Werror=array-bounds]
   15 |        __builtin_memset(&(data->in), 0, sizeof(struct DataIn));
      |        ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:4:8: note: subobject 'DataIn::type' declared here
    4 |    int type;
      |        ^~~~
cc1plus: all warnings being treated as errors
Compiler returned: 1


I reproduced the issue using https://godbolt.org/ so I verified that this happens with GCC 10.1 but not with GCC 9.3. It seems related to:
- nested structure
- having a "if" in the code
- "-Warray-bounds=2"
- "-fsanitize=address"

I know "-Warray-bounds=2" could have false positive but it's still a regression.
Comment 1 Frediano Ziglio 2020-07-21 13:14:23 UTC
The commit introducing this regression is:

commit 6889a3acfeed47265886676c6d43b04ef799fb82
Author: Martin Sebor <msebor@redhat.com>
Date:   Thu Sep 19 22:15:34 2019 +0000

    PR middle-end/91631 - buffer overflow into an array member of a declared object not detected
    
...
    
    From-SVN: r275981
Comment 2 Martin Sebor 2020-07-21 14:55:15 UTC
Confirmed.  The warning works as designed.  The cause of the problem, as in a number of other similar cases that have been reported recently (including pr94655 with the same root cause), is FRE substituting a smaller member for a larger member at the same address.  Before fre4 the IL corresponds to the access in the source code:

func (struct DataOut * data)
{
  int _1;
  struct DataIn * _2;
  int * _7;

  <bb 2> [local count: 1073741824]:
  _7 = &data_5(D)->in.type;
  .ASAN_CHECK (6, _7, 4, 4);
  _1 = data_5(D)->in.type;
  if (_1 != 0)
    goto <bb 3>; [33.00%]
  else
    goto <bb 4>; [67.00%]

  <bb 3> [local count: 354334800]:
  _2 = &data_5(D)->in;              <<< destination
  __builtin_memset (_2, 0, 8);      <<< okay

  <bb 4> [local count: 1073741824]:
  return;
}

Then fre4 turns the above into:

func (struct DataOut * data)
{
  int _1;
  int * _7;

  <bb 2> [local count: 1073741824]:
  _7 = &data_5(D)->in.type;         <<< destination
  .ASAN_CHECK (6, _7, 4, 4);
  _1 = data_5(D)->in.type;
  if (_1 != 0)
    goto <bb 3>; [33.00%]
  else
    goto <bb 4>; [67.00%]

  <bb 3> [local count: 354334800]:
  __builtin_memset (_7, 0, 8);      <<< warning

  <bb 4> [local count: 1073741824]:
  return;

}

There is nothing the warning can do to differentiate the result of this transformation from a bug in the source code.  To avoid compromising the utility of the warning, either the transformation needs to change or the IL needs to somehow/somewhere preserve the original destination of the access.  (I plan to look into the former.)

*** This bug has been marked as a duplicate of bug 94655 ***