This is the mail archive of the gcc-bugs@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Bug tree-optimization/70484] New: Wrong optimization with aliasing and access via char


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

            Bug ID: 70484
           Summary: Wrong optimization with aliasing and access via char
           Product: gcc
           Version: 6.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: tree-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: ch3root at openwall dot com
  Target Milestone: ---

The function:

int f(int *pi, long *pl)
{
  *pi = 1;             // (1)
  *pl = 0;             // (2)
  return *(char *)pi;  // (3)
}

is optimized (with -O2) to always return 1:

f:
        movl    $1, (%rdi)
        movl    $1, %eax
        movq    $0, (%rsi)
        ret

This is wrong if pi and pl both point to the same allocated block. In this case
the function should return 0.

The first impression could be that it's invalid to call this function with
equal arguments as it violates strict aliasing rules. This is wrong.

Suppose the function is called like this:

  void *p = malloc(sizeof(long));
  int result = f(p, p);

Then (1) is clearly Ok.

(2) is fine too because allocated memory can be repurposed freely. C11, 6.5p6,
reads: "If a value is stored into an object having no declared type through an
lvalue having a type that is not a character type, then the type of the lvalue
becomes the effective type of the object for that access and for subsequent
accesses that do not modify the stored value."

(3) is fine according to 6.5p7 because a character type can be used to access
anything.

Full example with a function:

----------------------------------------------------------------------
extern void *malloc (__SIZE_TYPE__);
extern void abort (void);

__attribute__((noinline,noclone))
int f(int *pi, long *pl)
{
  *pi = 1;
  *pl = 0;
  return *(char *)pi;
}

int main()
{
  void *p = malloc(sizeof(long));
  if (f(p, p) != 0)
    abort();
}
----------------------------------------------------------------------

Full example with volatile:

----------------------------------------------------------------------
extern void *malloc (__SIZE_TYPE__);
extern void abort (void);

int main()
{
  void *volatile p = malloc(sizeof(long));
  int *pi = p;
  long *pl = p;
  *pi = 1;
  *pl = 0;
  if (*(char *)pi != 0)
    abort();
}
----------------------------------------------------------------------

Tested on gcc 6.0.0 20160331. According to https://gcc.godbolt.org/ the bug is
present since at least 4.4.7.

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]