[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#429021: libc6: fputs can lose data in buffer on signal



Package: libc6
Version: 2.3.6.ds1-13
Severity: important


fputs (and probably other FILE based output functions) can lose
previously written data that were accumulated in the user space
buffer when a signal arrives. Here is an example that demonstrates
the problem:

# cat t.c
////////////////////////////////////////
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>


void handler(int sig)
{
    char buffer[80];

    snprintf(buffer, sizeof(buffer), "signal handler called, sig=%d\n", sig);
    write(2, buffer, strlen(buffer));
}

static int main_pid;

#define MYSIGNUM SIGINT

int reader(int fd)
{
    char buffer[1024];
    ssize_t count;
    size_t num_bytes, num_lines;

    sleep (1);
    kill (main_pid, MYSIGNUM);
    sleep (1);

    num_bytes = num_lines = 0;
    while ((count = read(fd, buffer, sizeof(buffer))) > 0)
    {
        char* p;
        for (p=buffer; p!=buffer+count; p++)
            if (*p =='\n')
                num_lines++;
        num_bytes += count;
    }
    if (count < 0)
    {
        perror ("read");
        return 1;
    }
    printf ("reader: num_bytes=%d num_lines=%d\n", num_bytes, num_lines);
    if (num_bytes != 8*10000) {
    	printf ("reader: number of missing bytes: %d\n", 80000 - num_bytes);
	return 1;
    }
    close(fd);
    return 0;
}

int writer(int fd)
{
    int i;
    size_t num_bytes, num_lines;
    FILE* f = fdopen(fd, "w");
    if (f == NULL) {
	    perror ("fdopen");
	    return 1;
    }
    num_bytes = num_lines = 0;
    for (i=0; i<10001; i++)
        if (fputs ("test...\n", f) == EOF)
        {
            printf ("error at num_bytes=%d\n", num_bytes);
            perror("fputs");
        }
	else
	{
		num_bytes += 8;
		num_lines ++;
	}
    if (fclose(f) == EOF) {
	    perror("fclose");
	    return 1;
    }
    printf ("writer: num_bytes=%d num_lines=%d\n", num_bytes, num_lines);
    if (num_bytes != 8*10000) {
    	printf ("writer: expected num_bytes=80000 but was %d\n", num_bytes);
	return 1;
    }
    return 0;
}

int main() {
    struct sigaction sa;
    int filedes[2];
    pid_t pid;
    int ret;

    main_pid = getpid();

    sa.sa_handler = handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sigaction(MYSIGNUM, &sa, NULL);

    if (pipe(filedes))
    {
        perror("pipe");
	    return 1;
    }

    pid=fork();
    if (pid == -1)
    {
        perror("fork");
        return 1;
    }
    if (pid==0)
    {
        close(filedes[1]);
        return reader(filedes[0]);
    }
    else
    {
        int status;
        close(filedes[0]);
        ret = writer(filedes[1]);
        wait(&status);
        return (ret==0 && status==0) ? 0 : 1;
    }
}

////////////////////////////////////////

# gcc -W -Wall t.c -o t && ./t
signal handler called, sig=2
error at num_bytes=69632
fputs: Interrupted system call
writer: num_bytes=80000 num_lines=10000
reader: num_bytes=75904 num_lines=9488
reader: number of missing bytes: 4096


-- System Information:
Debian Release: 4.0
  APT prefers stable
  APT policy: (500, 'stable')
Architecture: i386 (i686)
Shell:  /bin/sh linked to /bin/bash
Kernel: Linux 2.6.18-4-k7
Locale: LANG=ru_RU.UTF-8, LC_CTYPE=ru_RU.UTF-8 (charmap=UTF-8)

Versions of packages libc6 depends on:
ii  tzdata                        2007b-1    Time Zone and Daylight Saving Time

libc6 recommends no packages.

-- no debconf information



Reply to: