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

            Bug ID: 103343
           Summary: Invalid codegen when comparing pointer to one past the
                    end and then dereferencing that pointer
           Product: gcc
           Version: 12.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: gabravier at gmail dot com
  Target Milestone: ---

extern int x[1], y;

int f(int *p, int *q) {
    *q = y;
    if (p == (x + 1)) {
        *p = 2;
        return y;
    }
    return 0;
}

GCC trunk currently outputs the following code with -O3:

f:
        mov     eax, DWORD PTR y[rip]
        mov     DWORD PTR [rsi], eax
        cmp     rdi, OFFSET FLAT:x+4
        je      .L5
        xor     eax, eax
        ret
.L5:
        mov     DWORD PTR x[rip+4], 2
        ret

Which is incorrect because `p` could point to `y`, for example if `f` was
called as such:

int whatever;
f(&y, &whatever);

and `y` could happen to be located in memory right after `x`.

Also, although the comparison invokes unspecified behavior, this still means
only two results are possible according to the standard:
- if `p == (x + 1)` results in `false`, then the result of `f` is 0
- if `p == (x + 1)` results in `true`, then the result of `f` is 2 since we do
`*p = 2` and `p` points to `y`.

GCC's optimization makes it so the result can also be the previous value of
`y`, which could be something else than 0 or 2.

It seems that GCC assumes that because `p == (x + 1)` it can replace all
occurrences of `p` with `x + 1` without any regard to provenance, and doing
that change manually would indeed mean the `return y;` could be optimized to
use the previous store (and the store to `x + 1` would be UB, too...), but this
isn't the case here: `p` could simultaneously validly point to `y` and be equal
to `x + 1`.

PS: This also results in plenty of invalid warnings when compiling with -Wall:

<source>: In function 'f':
<source>:6:9: warning: array subscript 1 is outside array bounds of 'int[1]'
[-Warray-bounds]
    6 |         *p = 2;
      |         ^~
<source>:1:12: note: at offset 4 into object 'x' of size 4
    1 | extern int x[1], y;
      |            ^

Reply via email to