Bug 19965

Summary: Copy relocation leads to change in read-only data
Product: binutils Reporter: H.J. Lu <hjl.tools>
Component: ldAssignee: Not yet assigned to anyone <unassigned>
Status: RESOLVED DUPLICATE    
Severity: normal CC: amodra
Priority: P2    
Version: 2.27   
Target Milestone: 2.28   
Host: Target: x86
Build: Last reconfirmed:

Description H.J. Lu 2016-04-18 16:37:31 UTC
[hjl@gnu-6 relro-9]$ cat x.c
int const p1[1] = { -1 };
[hjl@gnu-6 relro-9]$ cat foo.c
#include <stdlib.h>

extern int const p1[1];

void
foo (void)
{
  if (p1[0] != -1)
    abort ();
}
[hjl@gnu-6 relro-9]$ cat m.c
#include <stdio.h>

extern void foo (void);

extern int p1[1];

int
main ()
{
  foo ();
  printf ("%d\n", p1[0]);
  p1[0] = 0;
  foo ();
  return 0;
}
[hjl@gnu-6 relro-9]$ make
gcc -O2 -g   -c -o m.o m.c
gcc -O2 -g -fPIC   -c -o x.o x.c
gcc -O2 -g -fPIC   -c -o foo.o foo.c
ld -shared --gc-sections -z relro -o libx.so x.o foo.o
gcc -o x -O2 -g m.o libx.so -Wl,-R,.
./x
-1
Makefile:10: recipe for target 'all' failed
make: *** [all] Aborted
[hjl@gnu-6 relro-9]$
Comment 1 Andreas Schwab 2016-04-18 17:22:13 UTC
And how is that a bug?  The program invokes undefined behavior.
Comment 2 H.J. Lu 2016-04-18 17:25:13 UTC
(In reply to Andreas Schwab from comment #1)
> And how is that a bug?  The program invokes undefined behavior.

Linker should generate copy relocation against read-only symbol
in .data.rel.ro section, instead of .bss section, so that the
copy of read-only symbol is also read-only after relocation at
run-time.
Comment 3 Alan Modra 2016-04-19 03:17:19 UTC
Here's a testcase that I think does not invoke any undefined behaviour, and shows various inconsistencies with protected variables and copy relocs.  Depending on the version of gcc (and glibc!) you may see &x != x or no segfault.
Compile protmain with -fPIC to see correct behaviour.

cat > prot.h <<\EOF
extern void *x;
extern void aprint (void);
extern void boom (void);
EOF
cat > prota.c <<\EOF
#include "prot.h"
void __attribute__ ((visibility ("protected"), section(".data.rel.ro")))
  *x = &x;
void aprint (void) { __builtin_printf ("A: &x = %p, x = %p\n", &x, x); }
EOF
cat > protb.c <<\EOF
#include "prot.h"
void boom (void) { x = 0; }
EOF
cat > protmain.c <<\EOF
#include "prot.h"
int
main (void)
{
  __builtin_printf ("main: &x = %p, x = %p\n", &x, x);
  aprint ();
  boom ();
  return 0;
}
EOF
gcc -O2 -fPIC -shared -o liba.so prota.c protb.c -Wl,-z,relro
gcc -O2 -o protmain protmain.c -L. -la -Wl,-rpath,.
./protmain
Comment 4 Alan Modra 2016-04-19 03:55:35 UTC
BTW, the testcase in comment #3 (and yours too!) show just how difficult it is to fix this problem.  When the main program is *not* relro you don't have anywhere to put the new .dynbss.ro or whatever you want to call the internal linker generated section for read-only .dynbss variables.  You don't have a .data.rel.ro in the main executable and can't tack them on to .rodata, because then ld.so will segfault when applying the copy relocation..
Comment 5 H.J. Lu 2017-01-12 17:33:26 UTC
Dup.

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