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

            Bug ID: 94658
           Summary: Incorrect transformation with -fstrict-aliasing
           Product: gcc
           Version: 9.3.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: pascal_cuoq at hotmail dot com
  Target Milestone: ---

C17 6.5:6 says:

The effective type of an object for an access to its stored value is the
declared type of the object, if any.88) 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. If
a value is copied into an object having no declared type using memcpy or
memmove, or is copied as an array of character type, then the effective type of
the modified object for that access and for subsequent accesses that do not
modify the value is the effective type of the object from which the value is
copied, if it has one. For all other accesses to an object having no declared
type, the effective type of the object is simply the type of the lvalue used
for the access.

I interpret the words “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” as saying that the
repurposing of dynamically allocated memory is allowed in C. A write of a new
value to dynamically allocated memory is not a “subsequent access that does not
modify the stored value”, so it simply changes the type of the memory to the
type of the lvalue being used for the write.

Consider the following program, which was provided by Sorav Bansal:

(Compiler Explorer link: https://gcc.godbolt.org/z/Vg25xq )

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

int a;

__attribute__((__noinline__)) int foo(float *x, int *y, int *z)
{
  int sum = 1;
  for (int i = 0; i < a; i++) {
    *z = 0x123;
    *x = 3.0f;
    sum *= *y;
    *z = 0x40400300;
  }
  return sum;
}

int main()
{
  char *f;
  f = malloc(32);
  if (!f) abort();
  int i = 3;
  a = 1;
  foo((float*)f, &i, (int*)f);
  float fl;
  memcpy(&fl, f, 4);
  printf("f = %f\n", fl);
}

According to my interpretation above (“repurposing of dynamically allocated
memory is allowed”), this program does nothing wrong. The memory location that
both x and z point to is repurposed many times, but it is never read inside
function foo, and the function main reads it carefully using memcpy.

The behavior I expect for this program is to print:

f = 3.000183

(3.000183 is the float represented by 0x40400300.)

However, when -fstrict-aliasing is enabled, the compiler moves the assignment
“*x = 3.0f;” after the loop and makes the compiled program print:

f = 3.000000

I think the transformation is wrong here. The function foo does not invoke UB
when passed z and x aliasing pointers to dynamically allocated memory, and the
“naïve memory model” behavior of the function should have been preserved for
such a calling context.

Yes, this would mean that Clang has independently implemented the same bug, but
I am still confident enough that the C standard intended to allow the
repurposing of dynamically allocated memory to report this. Please accept my
apologies if I am wrong. I have heard it said that the C++ standard has a
different philosophy, but this is a bug report against the C compiler.

Reply via email to