Bug 16532

Summary: fdopen(..., "a"); fwrite(); ftello() sequence gives wrong answer
Product: glibc Reporter: Paul Pluzhnikov <ppluzhnikov>
Component: stdioAssignee: Siddhesh Poyarekar <siddhesh>
Status: RESOLVED FIXED    
Severity: normal CC: bugdal, siddhesh, tim.lammens
Priority: P2 Flags: fweimer: security-
Version: unspecified   
Target Milestone: ---   
Host: Target:
Build: Last reconfirmed:

Description Paul Pluzhnikov 2014-02-06 02:47:28 UTC
This is a regression from 2.15.

Test case below, does not assert with glibc-2.15, but asserts with current trunk.

If I use fopen() instead of fdopen() ["./a.out -o"] or if I fflush() after fwrite() ["./a.out -f"], then assert goes away.

/* --- cut --- */
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
  FILE *fp;
  size_t written;
  off_t off;
  int do_flush = 0, do_fopen = 0;
  int opt;
  const char *const fname = "/tmp/output";

  while ((opt = getopt (argc, argv, "fo")) != -1)
    {
      switch (opt)
        {
        case 'f': do_flush = 1; break;
        case 'o': do_fopen = 1; break;
        }
    }

  fp = fopen (fname, "w");
  written = fwrite ("abcabc", 1, 6, fp);
  assert (written == 6);

  fclose (fp);

  if (do_fopen)
    fp = fopen (fname, "a");
  else
    {
      int fd = open (fname, O_WRONLY, 0);
      assert (fd != -1);
      fp = fdopen (fd, "a");
    }

  assert (fp != NULL);

  written = fwrite ("ghi", 1, 3, fp);
  assert (written == 3);

  if (do_flush)
    fflush (NULL);

  off = ftello (fp);
  assert (off == 9);

  return 0;
}
/* --- cut --- */
Comment 1 Paul Pluzhnikov 2014-02-06 22:28:11 UTC
This broke with commit adb26faefe47b7d34c941cbfc193ca7a5fde8e3f:


 2012-09-28  Siddhesh Poyarekar  <siddhesh@redhat.com>
 
      [BZ #5298]
       * libio/fileops.c (_IO_new_file_seekoff): Don't flush buffer
       for ftell.  Compute offsets from write pointers instead.
       * libio/wfileops.c (_IO_wfile_seekoff): Likewise.
Comment 2 Siddhesh Poyarekar 2014-02-07 04:06:52 UTC
Thanks, I'll look into it.
Comment 3 Siddhesh Poyarekar 2014-02-21 04:43:03 UTC
*** Bug 16605 has been marked as a duplicate of this bug. ***
Comment 4 Siddhesh Poyarekar 2014-03-04 09:06:32 UTC
Fixed in master:

commit 000232b9bcbf194f1e5fd0ff380000f341505405
Author: Siddhesh Poyarekar <siddhesh@redhat.com>
Date:   Tue Mar 4 07:45:58 2014 +0530

    Separate ftell from fseek logic and avoid modifying FILE data (#16532)
    
    ftell semantics are distinct from fseek(SEEK_CUR) especially when it
    is called on a file handler that is not yet active.  Due to this
    caveat, much care needs to be taken while modifying the handler data
    and hence, this first iteration on separating out ftell focusses on
    maintaining handler data integrity at all times while it figures out
    the current stream offset.  The result is that it makes a syscall for
    every offset request.
    
    There is scope for optimizing this by caching offsets when we know
    that the handler is active.  A simple way to find out is when the
    buffers have data.  It is not so simple to find this out when the
    buffer is empty without adding some kind of flag.
Comment 5 Tim 2014-09-10 19:50:09 UTC
I am afraid the provided fix has caused a memory leak.
See https://sourceware.org/bugzilla/show_bug.cgi?id=17370.
The malloc in the patch has not been freed.