- Subject: libc/1174: stdio without SA_RESTART loses output
- From: Keith Owens <kaos@ocs.com.au>
- Date: Fri Jun 25 19:06:39 1999
Topics:
libc/1174: stdio without SA_RESTART loses output
----------------------------------------------------------------------
Date: Tue, 22 Jun 1999 08:55:44 +1000
From: Keith Owens <kaos@ocs.com.au>
To: bugs@gnu.org
Subject: libc/1174: stdio without SA_RESTART loses output
Message-Id: <19990621225546.387.qmail@mail.ocs.com.au>
>Number: 1174
>Category: libc
>Synopsis: A program using stdio and signals without SA_RESTART loses output
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: libc-gnats
>State: open
>Class: sw-bug
>Submitter-Id: unknown
>Arrival-Date: Mon Jun 21 19:00:02 EDT 1999
>Last-Modified:
>Originator: Keith Owens
>Organization:
O. C. Software
>Release: libc-2.1.1
>Environment:
Host type: i386-redhat-linux-gnu
System: Linux ocs4 2.2.10 #1 Mon Jun 14 18:11:03 EST 1999 i586 unknown
Architecture: i586
Addons: crypt glibc-compat linuxthreads
Build CFLAGS: -O3 -Wall -Winline -Wstrict-prototypes -Wwrite-strings -g
Build CC: egcs
Compiler version: egcs-2.91.66 19990314/Linux (egcs-1.1.2 release)
Kernel headers: 2.2.10
Symbol versioning: yes
Build static: yes
Build shared: yes
Build pic-default: no
Build profile: yes
Build omitfp: no
Build bounded: no
Build static-nss: no
Stdio: libio
>Description:
A program using stdio and a signal handler which is defined
without SA_RESTART will drop 4K chunks of output when it
receives a signal at just the wrong time.
This was noticed while using prcs and piping the output into a
pager. Sometimes the output was invalid, with large bits
missing. The problem was tracked to the child signal handler
which was defined with sa_flags = 0. Changing sa_flags to
SA_RESTART appears to fix the problem, at least I cannot
reproduce with SA_RESTART.
Is it a requirement that all programs using stdio must use
SA_RESTART on all their signal handlers? If so, it is not
documented anywhere that I can find. If not, we have a bug.
>How-To-Repeat:
Compile (gcc bug.c -o bug) and run (./bug 2>&1 | less) this program.
*Slowly* scroll down the output, after about 6 seconds you will see the
"Childless!" line. Most of the time, one or more 4K chunks of output
go missing. Not every time, you have to hit stdio at just the right
point. Typical output is
mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm ...
nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn ...
j 24 c 512 ferror 0 errno 0
j 25 c 512 ferror 0 errno 0
j 26 c 512 ferror 0 errno 0
j 27 c 512 ferror 0 errno 0
j 28 c 512 ferror 0 errno 0
j 29 c 512 ferror 0 errno 0
j 30 c 512 ferror 0 errno 0
j 31 c 512 ferror 0 errno 0
Childless!
j 32 c 0 ferror 1 errno 4
j 32 c 512 ferror 0 errno 0
j 33 c 512 ferror 0 errno 0
j 34 c 512 ferror 0 errno 0
j 35 c 512 ferror 0 errno 0
j 36 c 512 ferror 0 errno 0
j 37 c 512 ferror 0 errno 0
j 38 c 512 ferror 0 errno 0
j 39 c 512 ferror 0 errno 0
wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww ...
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...
With sa-flags = 0, the program sees EINTR and redrives the fwrite loop
but it looks very much as though glibc stdio is not redriving its write
loop.
It does not occur if the program writes 4K chunks (data[4096]) itself.
Neither does it occur if sa_flags are set to SA_RESTART.
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
static void chld_handler(int s)
{
fprintf(stderr, "Childless!\n");
}
static void handle_signals(void)
{
struct sigaction act;
sigset_t signal_mask;
sigemptyset(&signal_mask);
act.sa_handler = chld_handler;
act.sa_mask = signal_mask;
act.sa_flags = 0; /* 0 bad, SA_RESTART good */
sigaction (SIGCHLD, &act, NULL);
}
int main(int argc, char **argv)
{
char *buf, data[512];
int i = 0, j, c, nbytes;
char fill[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
handle_signals();
if (!fork()) {
sleep(5);
return(0);
}
for (j = 0; j < 100; ++j) {
memset(data, fill[i], sizeof(data)-1);
data[sizeof(data)-1] = '\n';
if (++i >= sizeof(fill))
i = 0;
nbytes = sizeof(data);
buf = data;
while (nbytes > 0) {
do {
errno = 0;
clearerr(stdout);
c = fwrite (buf, 1, nbytes, stdout);
fprintf(stderr, "j %d c %d ferror %d errno %d\n", j, c, ferror(stdout), errno);
} while (c <= 0 && ferror(stdout) && errno == EINTR);
if (c <= 0 && ferror(stdout))
return 1;
nbytes -= c;
buf = buf + c;
}
}
return 0;
}
>Fix:
Use SA_RESTART on all signal handlers in all programs using
stdio. If this is a permanent requirement, it needs to be
prominently documented in both libc and stdio info/man pages.
>Audit-Trail:
>Unformatted:
------------------------------
End of forwardyKA-Cb Digest
***************************