bin/76398: stdio can lose data in the presence of signals
Yar Tikhiy
yar at comp.chem.msu.su
Tue Jan 18 01:50:25 PST 2005
>Number: 76398
>Category: bin
>Synopsis: stdio can lose data in the presence of signals
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: sw-bug
>Submitter-Id: current-users
>Arrival-Date: Tue Jan 18 09:50:24 GMT 2005
>Closed-Date:
>Last-Modified:
>Originator: Yar Tikhiy
>Release: FreeBSD 6.0-CURRENT i386
>Organization:
Moscow State University
>Environment:
System: FreeBSD stylish.chem.msu.su 6.0-CURRENT FreeBSD 6.0-CURRENT #0: Mon Jan 10 21:00:34 MSK 2005 yar at stylish.chem.msu.su:/usr/obj/usr/src/sys/STYLISH i386
>Description:
A signal handler can be installed without the SA_RESTART flag so
that the signal will interrupt certain syscalls. The application
should be able to restart the interrupted operation by itself if
it installs such signal handlers. However, stdio isn't ready for
this approach. An interrupted write call will be treated by stdio
as an unrecoverable error condition and the current buffer contents
will be lost. This is not dictated by the design, but is due to
the manner stdio has been coded in. See __sflush() in particular,
which is the major source of the problem.
>How-To-Repeat:
Here's a test program demonstrating the bug easily.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <err.h>
#include <errno.h>
#include <md5.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* #define CHILD_READER */
/* #define USE_PIPE */
#define NDATA 1000
#define DELAY 2
void hup(int);
void setdata(int);
char *getdata(void);
int
main()
{
char c, digest0[33], digest[33], *p;
FILE *fp;
int i, s[2];
MD5_CTX md5;
pid_t child;
struct sigaction sa;
MD5Init(&md5);
setdata(NDATA);
while ((p = getdata()))
MD5Update(&md5, p, strlen(p));
p = MD5End(&md5, digest0);
printf("True digest is %s\n", digest0);
sa.sa_handler = hup;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGHUP, &sa, NULL) == -1)
err(2, "sigaction");
#ifndef USE_PIPE
if (socketpair(PF_UNIX, SOCK_STREAM, 0, s) < 0)
err(2, "socketpair");
#else
if (pipe(s) < 0)
err(2, "pipe");
#endif
switch (child = fork()) {
case -1:
err(2, "fork");
#ifndef CHILD_READER
case 0:
#else
default:
#endif
if ((fp = fdopen(s[0], "w")) == NULL)
err(2, "fdopen in writer");
close(s[1]);
setdata(NDATA);
while ((p = getdata()))
for (; *p; p++)
if (fputc(*p, fp) == EOF) {
if (errno == EINTR)
clearerr(fp);
else
err(2, "fputc");
}
fclose(fp);
#ifdef CHILD_READER
waitpid(child, &i, 0);
#endif
break;
#ifndef CHILD_READER
default:
#else
case 0:
#endif
close(s[0]);
if ((fp = fdopen(s[1], "r")) == NULL)
err(2, "fdopen in reader");
sleep(DELAY);
#ifndef CHILD_READER
if (kill(child, SIGHUP) == -1)
#else
if (kill(getppid(), SIGHUP) == -1)
#endif
err(2, "kill");
sleep(DELAY);
MD5Init(&md5);
while ((i = fgetc(fp)) != EOF) {
c = i;
MD5Update(&md5, &c, 1);
}
MD5End(&md5, digest);
printf(" Got digest of %s\n", digest);
fclose(fp);
break;
}
return (0);
}
void
hup(int signo __unused)
{
}
static int ndata, seq;
void
setdata(int n)
{
ndata = n;
seq = 0;
}
char *
getdata()
{
static char databuf[256];
if (seq >= ndata)
return (NULL);
sprintf(databuf, "%08d xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n", seq++);
return (databuf);
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
>Fix:
I'm trying to work one out.
>Release-Note:
>Audit-Trail:
>Unformatted:
More information about the freebsd-bugs
mailing list