bug-coreutils
[Top][All Lists]
Advanced

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

Re: Bug#370583: cat -vte /proc/self/cmdline


From: Jim Meyering
Subject: Re: Bug#370583: cat -vte /proc/self/cmdline
Date: Wed, 07 Jun 2006 18:07:04 +0200

Dan Jacobson <address@hidden> wrote:
> Package: coreutils
> Version: 5.94-1
>
> I don't understand why this happens:
> $ cat /proc/self/cmdline|wc
>       0       1      23
> $ cat -e /proc/self/cmdline|wc
>       0       0       0
> $ cat -v /proc/self/cmdline|wc
>       0       0       0
> $ cat -t /proc/self/cmdline|wc
>       0       0       0
> $ cat /proc/self/cmdline > y
> $ cat -e y|wc
>       0       1      25

Hi Dan,

Thank you for reporting that.
It affects all other files in /proc, too, and those in /sys.

cat has an optimization that uses the FIONREAD ioctl.
There is a bug in cat.c (even the latest from the upstream development
trunk) that is triggered when that ioctl returns a value that doesn't
agree with how the following read call works.

The ioctl succeeds, but reports the number of bytes as the negative of
the actual value (this depends on your kernel).  For files in /sys,
the ioctl always reports a positive number, even when there's
nothing to read, and that too triggers the bug.  These using Debian's
2.6.16-2-686-smp kernel.

This is a race condition bug, too.
Small window, but easy to reproduce in the debugger.

Here's the patch I'll be applying to the trunk and
the stable branch (tests coming, too):

2006-06-07  Jim Meyering  <address@hidden>

        Ensure that cat works with any of the options, -A -v -e -E -T,
        when applied to files in /proc and /sys, even when the FIONREAD
        ioctl produces nonsensical results.  Before this change, cat would
        produce no output (or truncated output), for some linux kernels.

        * src/cat.c (write_pending): New function, factored out of cat.
        (cat): Also interpret a negative ioctl/FIONREAD count as indicating
        that there are bytes to read.  Some versions of linux-2.6.16 do that.
        Write any pending output before returning.
        Reported by Dan Jacobson in <http://bugs.debian.org/370583>.
        * NEWS: Mention this bug fix.

Index: cat.c
===================================================================
RCS file: /fetish/cu/src/cat.c,v
retrieving revision 1.107
diff -u -p -r1.107 cat.c
--- cat.c       14 Dec 2005 18:09:04 -0000      1.107
+++ cat.c       7 Jun 2006 15:13:00 -0000
@@ -1,5 +1,5 @@
 /* cat -- concatenate files and print on the standard output.
-   Copyright (C) 88, 90, 91, 1995-2005 Free Software Foundation, Inc.
+   Copyright (C) 88, 90, 91, 1995-2006 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -195,6 +195,22 @@ simple_cat (
     }
 }
 
+/* Write any pending output to STDOUT_FILENO.
+   Pending is defined to be the *BPOUT - OUTBUF bytes starting at OUTBUF.
+   Then set *BPOUT to OUTPUT if it's not already that value.  */
+
+static inline void
+write_pending (char *outbuf, char **bpout)
+{
+  size_t n_write = *bpout - outbuf;
+  if (0 < n_write)
+    {
+      if (full_write (STDOUT_FILENO, outbuf, n_write) != n_write)
+        error (EXIT_FAILURE, errno, _("write error"));
+      *bpout = outbuf;
+    }
+}
+
 /* Cat the file behind INPUT_DESC to the file behind OUTPUT_DESC.
    Return true if successful.
    Called if any option more than -u was specified.
@@ -291,6 +307,7 @@ cat (
 
          if (bpin > eob)
            {
+             bool input_pending = false;
 #ifdef FIONREAD
              int n_to_read = 0;
 
@@ -318,15 +335,12 @@ cat (
                      return false;
                    }
                }
-             if (n_to_read == 0)
+             if (n_to_read != 0)
+               input_pending = true;
 #endif
-               {
-                 size_t n_write = bpout - outbuf;
 
-                 if (full_write (STDOUT_FILENO, outbuf, n_write) != n_write)
-                   error (EXIT_FAILURE, errno, _("write error"));
-                 bpout = outbuf;
-               }
+             if (input_pending)
+               write_pending (outbuf, &bpout);
 
              /* Read more input into INBUF.  */
 
@@ -334,11 +348,13 @@ cat (
              if (n_read == SAFE_READ_ERROR)
                {
                  error (0, errno, "%s", infile);
+                 write_pending (outbuf, &bpout);
                  newlines2 = newlines;
                  return false;
                }
              if (n_read == 0)
                {
+                 write_pending (outbuf, &bpout);
                  newlines2 = newlines;
                  return true;
                }




reply via email to

[Prev in Thread] Current Thread [Next in Thread]