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

Bug#429021: marked as done (libc6: fputs can lose data in buffer on signal)



Your message dated Fri, 15 Jun 2007 18:09:42 +0200
with message-id <20070615160942.GD28554@artemis.intersec.eu>
and subject line Bug#429021: libc6: fputs can lose data in buffer on signal
has caused the attached Bug report to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what I am
talking about this indicates a serious mail system misconfiguration
somewhere.  Please contact me immediately.)

Debian bug tracking system administrator
(administrator, Debian Bugs database)

--- Begin Message ---
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


--- End Message ---
--- Begin Message ---
On Fri, Jun 15, 2007 at 05:10:59PM +0400, Dmitry Potapov wrote:
> 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;

  The bug is here, you should set the flags to SA_RESTART so that
interupted syscalls are restarted. If you don't do so you'll break the
libio.

  you can check using sigaction to fetch default state that SA_RESTART
is the default state for sa_flags, so you are knowingly breaking your
application.

  THe porable way to ignore a signal is to :

  sigaction(SIGINT, NULL, &sa);
  sa.sa_handler = your_handler;
  sigempty(sa.sa_mask);
  sigaction(SIGINT, &sa, NULL);

  with that, when the kernel return -ESTARTSYS syscalls are restarted.


-- 
·O·  Pierre Habouzit
··O                                                madcoder@debian.org
OOO                                                http://www.madism.org

Attachment: pgpJGtZ9qcm19.pgp
Description: PGP signature


--- End Message ---

Reply to: