Bug 1866474 - gcc 10.2 stores global const pointer in read-only memory area, regression from gcc 9.1
Summary: gcc 10.2 stores global const pointer in read-only memory area, regression fro...
Keywords:
Status: CLOSED NOTABUG
Alias: None
Product: Fedora
Classification: Fedora
Component: gcc
Version: 32
Hardware: x86_64
OS: Linux
unspecified
high
Target Milestone: ---
Assignee: Jakub Jelinek
QA Contact: Fedora Extras Quality Assurance
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2020-08-05 16:21 UTC by Gus Wirth
Modified: 2020-10-05 08:29 UTC (History)
10 users (show)

Fixed In Version:
Doc Type: If docs needed, set a value
Doc Text:
Clone Of:
Environment:
Last Closed: 2020-08-05 17:20:21 UTC
Type: Bug
Embargoed:


Attachments (Terms of Use)

Description Gus Wirth 2020-08-05 16:21:32 UTC
Description of problem:

gcc 10.2 generates code that segfaults due to placing global const pointers in read-only memory area rather than read-write as previous compilers

Version-Release number of selected component (if applicable):

gcc (GCC) 10.2.1 20200723 (Red Hat 10.2.1-1)

How reproducible:

Compile program with a global const pointer prototype to be initialized later.

Additional info:

Here is the simplest program I could come up with to demonstrate the 
problem. I call the program "maptest.c" because I'm looking at where 
things get mapped to memory by looking in /proc/<pid>/maps and seeing 
what permissions each chunk of memory has and where it is located. Using 
the debugger I can tell where in that memory space my variables are located.

Here's the program:


/* A program to try and find where a SIGSEV error occurs as
  * part of the program DOSEMU
  */

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

char * const lowmem_base;

int main(int argc, char **argv)

{

   void *addr = malloc(114112);

   *(char **)(&lowmem_base) = addr;

   printf("Hello World!\n");

   free(addr);
}

I could probably delete the Hello World! to make it even smaller but 
it's an experiment.

When I compile this with gcc like this:

$ gcc -g -Wall -o maptest maptest.c

I get an ELF executable called "maptest" with debug information as expected.

When I compile the program with gcc version 9.1.2 as found in Fedora 31, 
the programs runs and exits normally. Running in the debugger shows that 
the variable lowmem_base is located in a portion of the maptest space 
that is marked rw-p. Activating the disassembler in gdb shows that the 
value of addr is in a register and moves to the memory location of 
lowmem_base.

When I compile the program with gcc version 10.2.1 as found in Fedora 
32, the program runs and segfaults on the line:

   *(char **)(&lowmem_base) = addr;

Running in the debugger shows that the variable lowmem_base is located 
in a region of maptest marked as read only.

If I copy the binary from Feodra 31 to Fedora 32 it runs fine so I don't 
think it has anything to do with any of the shared libraries.

The equivalent of this program has been working since before 2012, so I 
think that there is a problem with gcc.

If I compile the program with clang, it functions as expected without a segfault.

Comment 1 Jakub Jelinek 2020-08-05 17:20:21 UTC
The testcase is invalid.
See e.g. ISO C99, 6.7.3/5: "If an attempt is made to modify an object defined with a const-qualified type through use
of an lvalue with non-const-qualified type, the behavior is undefined."
The only reason this "worked" before GCC 10 is that older GCC versions defaulted for C to -fcommon and as the variable has no initializer,
it has been a common symbol which is always writable.  If you compile this testcase with -fno-common, even GCC 9 or 8 will segfault on it.
If you compile with C++, it will not even compile.
You can use various workarounds, like -fcommon, or add __attribute__((common)) to the var, but the right fix is to drop the const qualifier if you want to modify the variable.


Note You need to log in before you can comment on or make changes to this bug.