Linux 2.3.3[234] FIFOs (named pipes) are unreliable

Markus Schoder (markus.schoder@inetmail.de)
Thu, 23 Dec 1999 01:47:47 +0100 (MET)


FIFOs (named pipes) are unreliable in Linux 2.3.33
(probably older kernels too). When doing the following

Terminal 1:

$ mkfifo /tmp/f
$ cat /tmp/f

Terminal 2:

$ echo hello >/tmp/f

I would have expected that the cat does now produce `hello' instead it
just sits there (most of the time). After several echo invocations the
cat will output everything at once and then exit.

I don't have the POSIX specs but this behaviour looks broken.

The reason for this behaviour is a race condition in fifo_open which the
following patch attempts to fix. Note that this race condition is
probably much more likely to trigger on an UP system.

What happens is that between the interruptible_sleep_on call of the reader
(the cat command in the example) an the down for the pipe semaphore, the
writer (the echo command) does write to the pipe and close the pipe. The
reader then checks the writer count, finds it to be zero and continues to
sleep.

I have already send a patch in a previous posting which is broken with
respect to signal handling.

Here is a new patch. This one extends the pipe_inode_info struct :( but I
don't see how to get signal handling right otherwise.

The patch is against 2.3.33 but should apply to 2.3.34 too.

(Please reply by email since I currently don't have access to the mailing
list).

--
Markus

--- /tmp/linux/fs/fifo.c Wed Dec 15 13:18:28 1999 +++ linux/fs/fifo.c Thu Dec 23 01:32:22 1999 @@ -41,6 +41,7 @@ PIPE_BASE(*inode) = (char *) page; PIPE_START(*inode) = PIPE_LEN(*inode) = 0; PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0; + PIPE_CONNECT(*inode) = 0; } switch (filp->f_mode) { @@ -51,11 +52,13 @@ * opened, even when there is no process writing the FIFO. */ filp->f_op = &connecting_fifo_fops; - if (PIPE_READERS(*inode)++ == 0) + if (PIPE_READERS(*inode)++ == 0 && PIPE_WRITERS(*inode)) { + PIPE_CONNECT(*inode) = 1; wake_up_interruptible(PIPE_WAIT(*inode)); + } if (!(filp->f_flags & O_NONBLOCK)) { - while (!PIPE_WRITERS(*inode)) { + while (!PIPE_CONNECT(*inode)) { if (signal_pending(current)) goto err_rd; up(PIPE_SEM(*inode)); @@ -69,7 +72,7 @@ } } - if (PIPE_WRITERS(*inode)) + if (PIPE_CONNECT(*inode)) filp->f_op = &read_fifo_fops; break; @@ -80,14 +83,16 @@ * errno=ENXIO when there is no process reading the FIFO. */ ret = -ENXIO; - if ((filp->f_flags & O_NONBLOCK) && !PIPE_READERS(*inode)) + if ((filp->f_flags & O_NONBLOCK) && !PIPE_CONNECT(*inode)) goto err; filp->f_op = &write_fifo_fops; - if (!PIPE_WRITERS(*inode)++) + if (!PIPE_WRITERS(*inode)++ && PIPE_READERS(*inode)) { + PIPE_CONNECT(*inode) = 1; wake_up_interruptible(PIPE_WAIT(*inode)); + } - while (!PIPE_READERS(*inode)) { + while (!PIPE_CONNECT(*inode)) { if (signal_pending(current)) goto err_wr; up(PIPE_SEM(*inode)); @@ -107,8 +112,10 @@ PIPE_READERS(*inode)++; PIPE_WRITERS(*inode)++; - if (PIPE_READERS(*inode) == 1 || PIPE_WRITERS(*inode) == 1) + if (PIPE_READERS(*inode) == 1 || PIPE_WRITERS(*inode) == 1) { + PIPE_CONNECT(*inode) = 1; wake_up_interruptible(PIPE_WAIT(*inode)); + } break; default: --- /tmp/linux/include/linux/pipe_fs_i.h Wed Dec 15 13:18:18 1999 +++ linux/include/linux/pipe_fs_i.h Thu Dec 23 01:43:02 1999 @@ -9,6 +9,7 @@ unsigned int writers; unsigned int waiting_readers; unsigned int waiting_writers; + unsigned int connect; }; /* Differs from PIPE_BUF in that PIPE_SIZE is the length of the actual @@ -24,6 +25,7 @@ #define PIPE_WRITERS(inode) ((inode).i_pipe->writers) #define PIPE_WAITING_READERS(inode) ((inode).i_pipe->waiting_readers) #define PIPE_WAITING_WRITERS(inode) ((inode).i_pipe->waiting_writers) +#define PIPE_CONNECT(inode) ((inode).i_pipe->connect) #define PIPE_EMPTY(inode) (PIPE_LEN(inode) == 0) #define PIPE_FULL(inode) (PIPE_LEN(inode) == PIPE_SIZE)

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/