about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSiddhesh Poyarekar <siddhesh@sourceware.org>2024-08-13 21:00:06 -0400
committerSiddhesh Poyarekar <siddhesh@sourceware.org>2024-08-29 20:35:00 -0400
commite930b89df7c263f3ceec20f085e06723fd1cb562 (patch)
treed362eb916cc0d34df7a7176bb620937d3203664e
parenta3db6ce75153141f0550de12b7681c07caebf16f (diff)
downloadglibc-e930b89df7c263f3ceec20f085e06723fd1cb562.tar.gz
glibc-e930b89df7c263f3ceec20f085e06723fd1cb562.tar.xz
glibc-e930b89df7c263f3ceec20f085e06723fd1cb562.zip
ungetc: Fix uninitialized read when putting into unused streams [BZ #27821]
When ungetc is called on an unused stream, the backup buffer is
allocated without the main get area being present.  This results in
every subsequent ungetc (as the stream remains in the backup area)
checking uninitialized memory in the backup buffer when trying to put a
character back into the stream.

Avoid comparing the input character with buffer contents when in backup
to avoid this uninitialized read.  The uninitialized read is harmless in
this context since the location is promptly overwritten with the input
character, thus fulfilling ungetc functionality.

Also adjust wording in the manual to drop the paragraph that says glibc
cannot do multiple ungetc back to back since with this change, ungetc
can actually do this.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
(cherry picked from commit cdf0f88f97b0aaceb894cc02b21159d148d7065c)
(cherry picked from commit 804d3c8db79db204154dcf5e11a76f14fdddc570)
-rw-r--r--libio/genops.c2
-rw-r--r--manual/stdio.texi8
-rw-r--r--stdio-common/tst-ungetc.c2
3 files changed, 6 insertions, 6 deletions
diff --git a/libio/genops.c b/libio/genops.c
index 1b629eb695..c27b3b1f41 100644
--- a/libio/genops.c
+++ b/libio/genops.c
@@ -635,7 +635,7 @@ _IO_sputbackc (FILE *fp, int c)
 {
   int result;
 
-  if (fp->_IO_read_ptr > fp->_IO_read_base
+  if (fp->_IO_read_ptr > fp->_IO_read_base && !_IO_in_backup (fp)
       && (unsigned char)fp->_IO_read_ptr[-1] == (unsigned char)c)
     {
       fp->_IO_read_ptr--;
diff --git a/manual/stdio.texi b/manual/stdio.texi
index 753c50920d..9f94dd1e78 100644
--- a/manual/stdio.texi
+++ b/manual/stdio.texi
@@ -1474,11 +1474,9 @@ program; usually @code{ungetc} is used only to unread a character that
 was just read from the same stream.  @Theglibc{} supports this
 even on files opened in binary mode, but other systems might not.
 
-@Theglibc{} only supports one character of pushback---in other
-words, it does not work to call @code{ungetc} twice without doing input
-in between.  Other systems might let you push back multiple characters;
-then reading from the stream retrieves the characters in the reverse
-order that they were pushed.
+@Theglibc{} supports pushing back multiple characters; subsequently
+reading from the stream retrieves the characters in the reverse order
+that they were pushed.
 
 Pushing back characters doesn't alter the file; only the internal
 buffering for the stream is affected.  If a file positioning function
diff --git a/stdio-common/tst-ungetc.c b/stdio-common/tst-ungetc.c
index 5c808f0734..388b202493 100644
--- a/stdio-common/tst-ungetc.c
+++ b/stdio-common/tst-ungetc.c
@@ -48,6 +48,8 @@ do_test (void)
   TEST_VERIFY_EXIT (getc (fp) == 'b');
   TEST_VERIFY_EXIT (getc (fp) == 'l');
   TEST_VERIFY_EXIT (ungetc ('m', fp) == 'm');
+  TEST_VERIFY_EXIT (ungetc ('n', fp) == 'n');
+  TEST_VERIFY_EXIT (getc (fp) == 'n');
   TEST_VERIFY_EXIT (getc (fp) == 'm');
   TEST_VERIFY_EXIT ((c = getc (fp)) == 'a');
   TEST_VERIFY_EXIT (getc (fp) == EOF);