Bug 703919 - gcov crashes if program alters its own environment
Summary: gcov crashes if program alters its own environment
Keywords:
Status: CLOSED NOTABUG
Alias: None
Product: Fedora
Classification: Fedora
Component: gcc
Version: 14
Hardware: Unspecified
OS: Unspecified
unspecified
unspecified
Target Milestone: ---
Assignee: Jakub Jelinek
QA Contact: Fedora Extras Quality Assurance
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2011-05-11 16:40 UTC by Eric Blake
Modified: 2011-05-17 15:41 UTC (History)
1 user (show)

Fixed In Version:
Doc Type: Bug Fix
Doc Text:
Clone Of:
Environment:
Last Closed: 2011-05-17 15:41:09 UTC
Type: ---
Embargoed:


Attachments (Terms of Use)

Description Eric Blake 2011-05-11 16:40:34 UTC
Description of problem:
The libvirt testsuite had a program that altered its own environment via assignment to environ.  When running this program under gcov, the program dumped core, even though the program had successfully reached exit().  Why?  Because gcov called getenv("GCOV_PREFIX") after the program's array for environ had gone out of scope.

For gcov to be useful, it should not let itself be affected by the operations of the program that it is tracing.  I think that gcov should cache all environment variables that it intends on reading up front, before starting main(), rather than re-reading them on every use case, so as to be immune to anything the traced program might do to its own environment.

Version-Release number of selected component (if applicable):
gcc-4.5.1-4.fc14.x86_64

How reproducible:
it was happening all the time for me with libvirt on the program tests/commandtest, until commit ba13a37 fixed the test to give environ a better scope.  I couldn't reproduce a crash on a simpler test case, but even then, gdb can show the problem

Steps to Reproduce:
Method 1: how I found the problem
1. git clone git://libvirt.org/libvirt.git
2. cd libvirt
3. git checkout ba13a37^
4. ./autobuild.sh

Method 2:
1. $ cat > bar.c <<\EOF
extern char **environ;
int main (void)
{
  const char *newenv[] = {
    "PATH=/usr/bin:/bin",
    0,
  };
  environ = (char **)newenv;
  return 0;
}
EOF
2. $ gcc -O2 -o bar -Wall bar.c -fprofile-arcs -ftest-coverage
3. $ gdb ./bar
4. (gdb) b main
5. (gdb) r
6. (gdb) b getenv
7. (gdb) c
8. (gdb) bt

Actual results:
Method 1: the libvirt testsuite fails, with a core dump in commandtest
Method 2: gcov calls getenv, after the contents of environ have gone out of scope, which has undefined behavior (even though if the program were not compiled for gcov, then there is no use of environ that late)
#0  getenv (name=0x4023e7 "GCOV_PREFIX") at getenv.c:36
#1  0x0000000000401370 in gcov_exit ()
#2  0x0000003d15e369f1 in __run_exit_handlers (status=0) at exit.c:78
#3  exit (status=0) at exit.c:100
#4  0x0000003d15e1ee64 in __libc_start_main (main=0x400c80 <main>, argc=1, 
    ubp_av=0x7fffffffe1c8, init=<value optimized out>, 
    fini=<value optimized out>, rtld_fini=<value optimized out>, 
    stack_end=0x7fffffffe1b8) at libc-start.c:258
#5  0x0000000000400bb9 in _start ()


Expected results:
Method 1: the libvirt test should succeed
Method 2: at step 7, the getenv() breakpoint should not hit after main() has started

Additional info:
https://www.redhat.com/archives/libvir-list/2011-May/msg00679.html

Comment 1 Jakub Jelinek 2011-05-17 09:15:40 UTC
This is clearly a bug in the testcase you are using, it is normal that there are various atexit handlers and disallowing getenv in all of them is a bug.

Comment 2 Eric Blake 2011-05-17 14:23:27 UTC
I agree that the code that originally exposed this problem was due to a bug in the program for preventing getenv() from working in an atexit handler, and that buggy program in libvirt has since been fixed to lengthen the lifetime of the new value assigned to environ.

However, I disagree with your assessment that this does not represent a real bug in gcov code - suppose I change the example in some other manner, without causing getenv() to fail in an atexit handler, but instead by changing the environment so that successive profile calls read different env var contents and corrupt the state expected by gcov.  That is, I think gcov is putting itself at risk by allowing user changes to environ to affect gcov's behavior; gcov would be safer by reading the environment once, up front, and using those cached values at all other points in the program where gcov is currently querying the environment again.

Comment 3 Jakub Jelinek 2011-05-17 15:41:09 UTC
libgcov is calling getenv solely during the atexit handler, unless you fork, thus it works just fine if the app changes the env vars, as long as getenv can be called.


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