[PATCH 5/6] Fix fflush handling for mmap files after ungetc (bug 32535)

Joseph Myers josmyers@redhat.com
Mon Jan 20 22:56:53 GMT 2025


On Wed, 15 Jan 2025, DJ Delorie wrote:

> Some of the logic seems a little off to me, but it might be my brand new
> understanding of how _IO_* works ;-)
> 
> Also, the test seems weak somehow.
> 
> Joseph Myers <josmyers@redhat.com> writes:
> > diff --git a/libio/fileops.c b/libio/fileops.c
> >  int
> >  _IO_file_sync_mmap (FILE *fp)
> >  {
> > +  off64_t o = fp->_offset - (fp->_IO_read_end - fp->_IO_read_ptr);
> 
> To be consistent with logic elsewhere, this could have been:
> 
> > off64_t o = fp->_offset + (fp->_IO_read_ptr - fp->_IO_read_end);

This is following _IO_file_seekoff_mmap.

> >    if (fp->_IO_read_ptr != fp->_IO_read_end)
> >      {
> > -      if (__lseek64 (fp->_fileno, fp->_IO_read_ptr - fp->_IO_buf_base,
> > -		     SEEK_SET)
> > -	  != fp->_IO_read_ptr - fp->_IO_buf_base)
> > +      if (_IO_in_backup (fp))
> > +	{
> > +	  _IO_switch_to_main_get_area (fp);
> > +	  o -= fp->_IO_read_end - fp->_IO_read_base;
> 
> Shouldn't one of these be _IO_read_ptr ?

_IO_switch_to_main_get_area includes "fp->_IO_read_ptr = 
fp->_IO_read_base;".  (This logic is actually emulating the use of 
"fp->_IO_save_end - fp->_IO_save_base" in ftell and related functions.)

> > +  fp = xfopen (filename, "rm");
> > +  TEST_COMPARE (fgetc (fp), 't');
> > +  TEST_COMPARE (ungetc ('u', fp), 'u');
> > +  TEST_COMPARE (fflush (fp), 0);
> 
> Should we test that fflush() did the right thing, somehow?
> Can we, for mmap'd files?

I've added tests of the next characters read from the file, which is more 
or less what it means for fflush to do the right thing here (discard the 
character pushed with ungetc while preserving the offset).




Fix fflush handling for mmap files after ungetc (bug 32535)

As discussed in bug 32535, fflush fails on files opened for reading
using mmap after ungetc.  Fix the logic to handle this case and still
compute the file offset correctly.

Tested for x86_64.

---

Changed in v2: also verify characters read after fflush.
---
 libio/fileops.c                | 12 +++++---
 stdio-common/Makefile          |  1 +
 stdio-common/tst-fflush-mmap.c | 50 ++++++++++++++++++++++++++++++++++
 3 files changed, 59 insertions(+), 4 deletions(-)
 create mode 100644 stdio-common/tst-fflush-mmap.c

diff --git a/libio/fileops.c b/libio/fileops.c
index 97875d1eaf..7e370d12b2 100644
--- a/libio/fileops.c
+++ b/libio/fileops.c
@@ -858,17 +858,21 @@ libc_hidden_ver (_IO_new_file_sync, _IO_file_sync)
 int
 _IO_file_sync_mmap (FILE *fp)
 {
+  off64_t o = fp->_offset - (fp->_IO_read_end - fp->_IO_read_ptr);
   if (fp->_IO_read_ptr != fp->_IO_read_end)
     {
-      if (__lseek64 (fp->_fileno, fp->_IO_read_ptr - fp->_IO_buf_base,
-		     SEEK_SET)
-	  != fp->_IO_read_ptr - fp->_IO_buf_base)
+      if (_IO_in_backup (fp))
+	{
+	  _IO_switch_to_main_get_area (fp);
+	  o -= fp->_IO_read_end - fp->_IO_read_base;
+	}
+      if (__lseek64 (fp->_fileno, o, SEEK_SET) != o)
 	{
 	  fp->_flags |= _IO_ERR_SEEN;
 	  return EOF;
 	}
     }
-  fp->_offset = fp->_IO_read_ptr - fp->_IO_buf_base;
+  fp->_offset = o;
   fp->_IO_read_end = fp->_IO_read_ptr = fp->_IO_read_base;
   return 0;
 }
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index 06c9eaf426..703e8af7e8 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -239,6 +239,7 @@ tests := \
   tst-fdopen2 \
   tst-ferror \
   tst-fflush-all-input \
+  tst-fflush-mmap \
   tst-fgets \
   tst-fgets2 \
   tst-fileno \
diff --git a/stdio-common/tst-fflush-mmap.c b/stdio-common/tst-fflush-mmap.c
new file mode 100644
index 0000000000..3bddb909e0
--- /dev/null
+++ b/stdio-common/tst-fflush-mmap.c
@@ -0,0 +1,50 @@
+/* Test fflush after ungetc on files using mmap (bug 32535).
+   Copyright (C) 2025 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+
+#include <support/check.h>
+#include <support/temp_file.h>
+#include <support/xstdio.h>
+#include <support/xunistd.h>
+
+int
+do_test (void)
+{
+  char *filename = NULL;
+  int fd = create_temp_file ("tst-fflush-mmap", &filename);
+  TEST_VERIFY_EXIT (fd != -1);
+  xclose (fd);
+
+  /* Test fflush after ungetc (bug 32535).  */
+  FILE *fp = xfopen (filename, "w");
+  TEST_VERIFY (0 <= fputs ("test", fp));
+  xfclose (fp);
+
+  fp = xfopen (filename, "rm");
+  TEST_COMPARE (fgetc (fp), 't');
+  TEST_COMPARE (ungetc ('u', fp), 'u');
+  TEST_COMPARE (fflush (fp), 0);
+  TEST_COMPARE (fgetc (fp), 't');
+  TEST_COMPARE (fgetc (fp), 'e');
+  xfclose (fp);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
-- 
2.43.0


-- 
Joseph S. Myers
josmyers@redhat.com



More information about the Libc-alpha mailing list