[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
undefined behavior in closeout, aggravated by libsigsegv
From: |
Eric Blake |
Subject: |
undefined behavior in closeout, aggravated by libsigsegv |
Date: |
Fri, 17 Jul 2009 20:47:17 +0000 (UTC) |
User-agent: |
Loom/3.14 (http://gmane.org/) |
I'm seeing the following in cygwin with m4 1.4.12, due to an interaction
between the closeout module and libsigsegv:
$ echo hi > file
$ build/src/m4 >&- file
build/src/m4: internal error detected; please report this bug to <bug-
address@hidden>: Segmentation fault
I've traced it to the fact that m4 uses the closeout module, which does the
following:
calls close_string (stdout)
=> fclose (stdout)
detects that pending data was lost, so call error()
=> fflush (stdout)
According to POSIX, using stdout after it has been passed to fclose is
undefined behavior. Normally, cygwin is able to do just fine when flushing an
already closed fd (basically, there is a fault under the hood, but the fault is
trapped and silently ignored). But when libsigsegv is thrown in the mix, the
fault is now caught by libsigsegv instead of cygwin, in a manner visible to the
application.
$ cat foo.c
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#ifdef libsigsegv
#include <sigsegv.h>
int
handler (void *addr, int bad)
{
abort ();
}
#endif
void
die ()
{
int i;
i = fclose (stdout);
fprintf (stderr, "fclose %d, errno %d\n", i, errno);
errno = 0;
i = fflush (stdout);
fprintf (stderr, "fflush %d, errno %d\n", i, errno);
}
int main ()
{
close (1);
atexit (die);
#ifdef libsigsegv
sigsegv_install_handler (handler);
#endif
fputc ('1', stdout);
return 0;
}
$ gdb -o foo foo.c -lsigsegv
$ ./foo
fclose -1, errno 9
fflush 0, errno 0
$ gdb -o foo foo.c -Dlibsigsegv -lsigsegv
$ ./foo
fclose -1, errno 9
Aborted (core dumped)
$
I'm not sure if there is a bug in libsigsegv, such that the under-the-hood
fault is now exposed to the application. At any rate, there is currently a
thread on the cygwin list about a crash under Windows 2008 R2 64-bit, where
Windows has tightened the rules on what forms a valid SEH exception chain and
the cygwin handler was violating those rules; and that thread has also raised
the concern that libsigsegv may be making the same mistakes by trying to
override cygwin's handler:
http://cygwin.com/ml/cygwin/2009-07/msg00630.html
http://cygwin.com/ml/cygwin/2009-07/msg00635.html
Contrast this to a run on Solaris without libsigsegv, where the fflush at least
sets errno to EBADF on an attempt to manipulate a closed stream (but has the
wrong return status):
% ./foo
fclose -1, errno 9
fflush 0, errno 9
If nothing else, cygwin should probably be taught to recognize a previously
closed file up front, rather than depending on catching an internal fault, and
set errno to EBADF instead of claiming success.
But even if cygwin is improved to not trigger an internal fault, and/or
libsigsegv changed to not interfere with cygwin's detection of internal faults,
it seems that the real root cause is that gnulib has caused the application to
perform undefined behavior during the atexit() handler, and that we should fix
this before it trips up other platforms. What's the best way to do that? Have
close_stdout call freopen("/dev/null","w",stdout) prior to calling error()?
Convince the glibc folks to change error() to not call fflush(stdout) if fileno
(stdout) is closed? Rewrite close_stdout to open-code the error-printing
actions instead of calling error()? Something else?
--
Eric Blake