Date: Tue, 18 Jan 2005 12:47:01 +0300 (MSK) From: Yar Tikhiy <yar@comp.chem.msu.su> To: FreeBSD-gnats-submit@FreeBSD.org Subject: bin/76398: stdio can lose data in the presence of signals Message-ID: <200501180947.j0I9l16Y033613@stylish.chem.msu.su> Resent-Message-ID: <200501180950.j0I9oOER077461@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>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@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:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200501180947.j0I9l16Y033613>