]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
Reset cached offset when reading to end of stream (BZ #17653)
authorSiddhesh Poyarekar <siddhesh@redhat.com>
Thu, 4 Dec 2014 02:43:28 +0000 (08:13 +0530)
committerSiddhesh Poyarekar <siddhesh@redhat.com>
Thu, 4 Dec 2014 02:43:28 +0000 (08:13 +0530)
POSIX allows applications to switch file handles when a read results
in an end of file.  Unset the cached offset at this point so that it
is queried again.

ChangeLog
NEWS
libio/fileops.c
libio/tst-ftell-active-handler.c
libio/wfileops.c

index 1bd4b23dc7ffe01c1b47c57d9a3cae298c7db1f0..4cee7d79c62a2c1d14dd1c6d673cd2c72daab49c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,15 @@
 2914-12-04  Siddhesh Poyarekar  <siddhesh@redhat.com>
 
+       [BZ #17653]
+       * libio/fileops.c (_IO_new_file_underflow): Unset cached
+       offset on EOF.
+       * libio/wfileops.c (_IO_wfile_underflow): Likewise.
+       * libio/tst-ftell-active-handler.c (fgets_func_t): New type.
+       (fgets_func): Function pointer to fgets and fgetws.
+       (do_ftell_test): Add test to verify ftell value after read
+       EOF.
+       (do_test): Set fgets_func.
+
        * libio/tst-ftell-active-handler.c (do_ftruncate_test): Add
        O_TRUNC flag for w and w+ modes.
        (do_rewind_test): Likewise.
diff --git a/NEWS b/NEWS
index 7312b47eb102e976215aac8407b2ebbf79f47661..84c135314898109ea20a5610688ead4292240b04 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -13,7 +13,7 @@ Version 2.21
   16619, 16740, 16857, 17192, 17266, 17344, 17363, 17370, 17371, 17411,
   17460, 17475, 17485, 17501, 17506, 17508, 17522, 17555, 17570, 17571,
   17572, 17573, 17574, 17581, 17582, 17583, 17584, 17585, 17589, 17594,
-  17601, 17608, 17616, 17625, 17633, 17647, 17664, 17665, 17668.
+  17601, 17608, 17616, 17625, 17633, 17647, 17653, 17664, 17665, 17668.
 
 * CVE-2104-7817 The wordexp function could ignore the WRDE_NOCMD flag
   under certain input conditions resulting in the execution of a shell for
index dca359179f0461799728b500317791776a526559..ee50b72aec0c78275984490985b008086b8a9a68 100644 (file)
@@ -615,7 +615,13 @@ _IO_new_file_underflow (fp)
   }
   fp->_IO_read_end += count;
   if (count == 0)
-    return EOF;
+    {
+      /* If a stream is read to EOF, the calling application may switch active
+        handles.  As a result, our offset cache would no longer be valid, so
+        unset it.  */
+      fp->_offset = _IO_pos_BAD;
+      return EOF;
+    }
   if (fp->_offset != _IO_pos_BAD)
     _IO_pos_adjust (fp->_offset, count);
   return *(unsigned char *) fp->_IO_read_ptr;
index 72066b467e85b8d3077ed046ede7bf3f8d200682..f69e16922a043c38182065d551d8ce21f7335e0d 100644 (file)
@@ -86,7 +86,9 @@ static size_t data_len;
 static size_t file_len;
 
 typedef int (*fputs_func_t) (const void *data, FILE *fp);
+typedef void *(*fgets_func_t) (void *ws, int n, FILE *fp);
 fputs_func_t fputs_func;
+fgets_func_t fgets_func;
 
 /* This test verifies that the offset reported by ftell is correct after the
    file is truncated using ftruncate.  ftruncate does not change the file
@@ -290,20 +292,22 @@ do_ftell_test (const char *filename)
       int fd_mode;
       size_t old_off;
       size_t new_off;
+      size_t eof_off;
     } test_modes[] = {
          /* In w, w+ and r+ modes, the file position should be at the
             beginning of the file.  After the write, the offset should be
-            updated to data_len.  */
-         {"w", O_WRONLY | O_TRUNC, 0, data_len},
-         {"w+", O_RDWR | O_TRUNC, 0, data_len},
-         {"r+", O_RDWR, 0, data_len},
+            updated to data_len.  We don't use eof_off in w and a modes since
+            they don't allow reading.  */
+         {"w", O_WRONLY | O_TRUNC, 0, data_len, 0},
+         {"w+", O_RDWR | O_TRUNC, 0, data_len, 2 * data_len},
+         {"r+", O_RDWR, 0, data_len, 3 * data_len},
          /* For the 'a' mode, the initial file position should be the
             current end of file. After the write, the offset has data_len
             added to the old value.  For a+ mode however, the initial file
             position is the file position of the underlying file descriptor,
             since it is initially assumed to be in read mode.  */
-         {"a", O_WRONLY, data_len, 2 * data_len},
-         {"a+", O_RDWR, 0, 3 * data_len},
+         {"a", O_WRONLY, 3 * data_len, 4 * data_len, 5 * data_len},
+         {"a+", O_RDWR, 0, 5 * data_len, 6 * data_len},
     };
   for (int j = 0; j < 2; j++)
     {
@@ -348,12 +352,44 @@ do_ftell_test (const char *filename)
 
          if (off != test_modes[i].new_off)
            {
-             printf ("Incorrect new offset.  Expected %zu but got %ld\n",
+             printf ("Incorrect new offset.  Expected %zu but got %ld",
                      test_modes[i].new_off, off);
              ret |= 1;
            }
          else
-           printf ("new offset = %ld\n", off);
+           printf ("new offset = %ld", off);
+
+         /* Read to the end, write some data to the fd and check if ftell can
+            see the new ofset.  Do this test only for files that allow
+            reading.  */
+         if (test_modes[i].fd_mode != O_WRONLY)
+           {
+             char tmpbuf[data_len];
+
+             rewind (fp);
+
+             while (fgets_func (tmpbuf, sizeof (tmpbuf), fp) && !feof (fp));
+
+             write_ret = write (fd, data, data_len);
+             if (write_ret != data_len)
+               {
+                 printf ("write failed (%m)\n");
+                 ret |= 1;
+               }
+             off = ftell (fp);
+
+             if (off != test_modes[i].eof_off)
+               {
+                 printf (", Incorrect offset after read EOF.  "
+                         "Expected %zu but got %ld\n",
+                         test_modes[i].eof_off, off);
+                 ret |= 1;
+               }
+             else
+               printf (", offset after EOF = %ld\n", off);
+           }
+         else
+           putc ('\n', stdout);
 
          fclose (fp);
        }
@@ -617,6 +653,7 @@ do_test (void)
   /* Tests for regular files.  */
   puts ("Regular mode:");
   fputs_func = (fputs_func_t) fputs;
+  fgets_func = (fgets_func_t) fgets;
   data = char_data;
   data_len = strlen (char_data);
   ret |= do_one_test (filename);
@@ -638,6 +675,7 @@ do_test (void)
       return 1;
     }
   fputs_func = (fputs_func_t) fputws;
+  fgets_func = (fgets_func_t) fgetws;
   data = wide_data;
   data_len = wcslen (wide_data);
   ret |= do_one_test (filename);
index 71281c1e5dded284671f83145d4e1c47b1617aed..2a003b368d6792cc1220e577044cebdde739ea72 100644 (file)
@@ -257,7 +257,10 @@ _IO_wfile_underflow (fp)
   if (count <= 0)
     {
       if (count == 0 && naccbuf == 0)
-       fp->_flags |= _IO_EOF_SEEN;
+       {
+         fp->_flags |= _IO_EOF_SEEN;
+         fp->_offset = _IO_pos_BAD;
+       }
       else
        fp->_flags |= _IO_ERR_SEEN, count = 0;
     }