Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can two different objects with automatic storage duration compare equal under address comparison?

Tags:

c

x86

clang

In particular, is it allowed for the addresses of two automatic variables in different functions to compare equal as follows:

sink.c

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

void sink(void *l, void *r) {
    puts(l == r ? "equal" : "not equal");
    exit(0);
}

main.c

typedef struct { char x[32]; } Foo;

void sink(void *l, void *r);

Foo make(void *p) {
    Foo f2;
    sink(&f2, p);
    return f2;
}

int main() {
    Foo f1 = make(&f1);
}

I would expect this to print not equal as f1 and f2 are distinct objects. With gcc I get not equal, but with my local version of clang 3.81, it prints equal, when compiled as clang -O1 sink.c main.c2.

Disassembling make and main ...

0000000000400570 <make>:
  400570:   53                      push   rbx
  400571:   48 89 fb                mov    rbx,rdi
  400574:   e8 d7 ff ff ff          call   400550 <sink>
  400579:   48 89 d8                mov    rax,rbx
  40057c:   5b                      pop    rbx
  40057d:   c3                      ret    
  40057e:   66 90                   xchg   ax,ax

0000000000400580 <main>:
  400580:   48 83 ec 28             sub    rsp,0x28
  400584:   48 8d 7c 24 08          lea    rdi,[rsp+0x8]
  400589:   48 89 fe                mov    rsi,rdi
  40058c:   e8 df ff ff ff          call   400570 <make>
  400591:   31 c0                   xor    eax,eax
  400593:   48 83 c4 28             add    rsp,0x28
  400597:   c3                      ret    

... we see that make never seems to create the Foo f2 object at all, it just calls sink with the existing rdi and rsi (the l and r parameters, respectively). These are passed by main and are the same: the first, rdi, is the hidden pointer to the location to put the return value, and the second is &f1, so we expect these to be the same.


1 I checked versions up to 7.0 and the behavior is roughly the same.

2 It happens for -O1, -O2 and -O3, but not -O0 which prints not equal instead.

like image 308
BeeOnRope Avatar asked Jun 03 '18 07:06

BeeOnRope


1 Answers

The C11 standard part 6.5.9/6 says:

Two pointers compare equal if and only if both are null pointers, both are pointers to the same object (including a pointer to an object and a subobject at its beginning) or function, both are pointers to one past the last element of the same array object, or one is a pointer to one past the end of one array object and the other is a pointer to the start of a different array object that happens to immediately follow the first array object in the address space.

In this code none of the listed conditions hold; &f1 and &f2 are pointers to different objects, and one is not a subobject of the other.

So the pointers must not compare equal. The compiler reporting equal is non-conforming.


Note: If anyone has doubts about the legality of Foo f1 = make(&f1);, see this question. It is fine and the automatic object's lifetime begins at the preceding {.

like image 87
M.M Avatar answered Oct 24 '22 06:10

M.M