[v4] libio: do not attempt to free wide buffers of legacy streams [BZ #24228]

Message ID 20190619195130.GA27256@altlinux.org
State New
Headers show
Series
  • [v4] libio: do not attempt to free wide buffers of legacy streams [BZ #24228]
Related show

Commit Message

Dmitry V. Levin June 19, 2019, 7:51 p.m.
Commit a601b74d31ca086de38441d316a3dee24c866305 aka glibc-2.23~693
("In preparation for fixing BZ#16734, fix failure in misc/tst-error1-mem
when _G_HAVE_MMAP is turned off.") introduced a regression:
_IO_unbuffer_all() now invokes _IO_wsetb() to free wide buffers of all
files, including legacy standard files which are small statically
allocated objects that do not have wide buffers and the _mode member,
causing memory corruption.

Another memory corruption in _IO_unbuffer_all() happens when -1
is assigned to the _mode member of legacy standard files that do not
have it.

[BZ #24228]
* libio/genops.c (_IO_unbuffer_all)
[SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1)]: Do not attempt to free wide
buffers and access _IO_FILE_complete members of legacy libio streams.
* libio/tst-bz24228.c: New file.
* libio/tst-bz24228.map: Likewise.
* libio/Makefile (tests): Add tst-bz24228.
(generated): Add tst-bz24228.mtrace and tst-bz24228.check.
(tests-special): Add $(objpfx)tst-bz24228-mem.out.
(LDFLAGS-tst-bz24228, tst-bz24228-ENV): New variables.
($(objpfx)tst-bz24228-mem.out): New rule.
---
 ChangeLog             | 14 ++++++++++++++
 libio/Makefile        | 11 ++++++++++-
 libio/genops.c        | 16 ++++++++++++----
 libio/tst-bz24228.c   | 29 +++++++++++++++++++++++++++++
 libio/tst-bz24228.map |  5 +++++
 5 files changed, 70 insertions(+), 5 deletions(-)
 create mode 100644 libio/tst-bz24228.c
 create mode 100644 libio/tst-bz24228.map

-- 
ldv

Comments

Florian Weimer June 19, 2019, 9:15 p.m. | #1
* Dmitry V. Levin:

> Commit a601b74d31ca086de38441d316a3dee24c866305 aka glibc-2.23~693

> ("In preparation for fixing BZ#16734, fix failure in misc/tst-error1-mem

> when _G_HAVE_MMAP is turned off.") introduced a regression:

> _IO_unbuffer_all() now invokes _IO_wsetb() to free wide buffers of all

> files, including legacy standard files which are small statically

> allocated objects that do not have wide buffers and the _mode member,

> causing memory corruption.

>

> Another memory corruption in _IO_unbuffer_all() happens when -1

> is assigned to the _mode member of legacy standard files that do not

> have it.


GNU style calls for not putting () after function names.  (Sorry, I must
sound like a broken record).

> diff --git a/libio/Makefile b/libio/Makefile

> index 7d53cb06b0..b81e8bf5e2 100644

> --- a/libio/Makefile

> +++ b/libio/Makefile

> @@ -65,7 +65,7 @@ tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc   \

>  	tst-setvbuf1 tst-popen1 tst-fgetwc bug-wsetpos tst-fseek \

>  	tst-fwrite-error tst-ftell-partial-wide tst-ftell-active-handler \

>  	tst-ftell-append tst-fputws tst-bz22415 tst-fgetc-after-eof \

> -	tst-sprintf-ub tst-sprintf-chk-ub tst-bz24051 tst-bz24153 \

> +	tst-sprintf-ub tst-sprintf-chk-ub tst-bz24051 tst-bz24153 tst-bz24228 \

>  	tst-wfile-sync tst-wfile-gconv


I suggest to qualify the new test with “ifeq ($(build-shared),yes)”.  It
can never meet its test objective with static linking because the
relevant code just is not there.

Rest looks good to me, thanks.

I can confirm that it fixes the OpenJDK failure we saw.

Thanks,
Florian

Patch

diff --git a/libio/Makefile b/libio/Makefile
index 7d53cb06b0..b81e8bf5e2 100644
--- a/libio/Makefile
+++ b/libio/Makefile
@@ -65,7 +65,7 @@  tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc   \
 	tst-setvbuf1 tst-popen1 tst-fgetwc bug-wsetpos tst-fseek \
 	tst-fwrite-error tst-ftell-partial-wide tst-ftell-active-handler \
 	tst-ftell-append tst-fputws tst-bz22415 tst-fgetc-after-eof \
-	tst-sprintf-ub tst-sprintf-chk-ub tst-bz24051 tst-bz24153 \
+	tst-sprintf-ub tst-sprintf-chk-ub tst-bz24051 tst-bz24153 tst-bz24228 \
 	tst-wfile-sync tst-wfile-gconv
 
 # This test tests interaction with the gconv cache.  Setting
@@ -162,16 +162,20 @@  CFLAGS-tst_putwc.c += -DOBJPFX=\"$(objpfx)\"
 CFLAGS-tst-sprintf-ub.c += -Wno-restrict
 CFLAGS-tst-sprintf-chk-ub.c += -Wno-restrict
 
+LDFLAGS-tst-bz24228 = -Wl,--version-script=tst-bz24228.map
+
 tst_wprintf2-ARGS = "Some Text"
 
 test-fmemopen-ENV = MALLOC_TRACE=$(objpfx)test-fmemopen.mtrace
 tst-fopenloc-ENV = MALLOC_TRACE=$(objpfx)tst-fopenloc.mtrace
 tst-bz22415-ENV = MALLOC_TRACE=$(objpfx)tst-bz22415.mtrace
+tst-bz24228-ENV = MALLOC_TRACE=$(objpfx)tst-bz24228.mtrace
 tst-wfile-gconv-ENV = MALLOC_TRACE=$(objpfx)tst-wfile-gconv.mtrace
 
 generated += test-fmemopen.mtrace test-fmemopen.check
 generated += tst-fopenloc.mtrace tst-fopenloc.check
 generated += tst-bz22415.mtrace tst-bz22415.check
+generated += tst-bz24228.mtrace tst-bz24228.check
 generated += tst-wfile-gconv.mtrace tst-wfile-gconv.check
 
 aux	:= fileops genops stdfiles stdio strops
@@ -188,6 +192,7 @@  shared-only-routines = oldiofopen oldiofdopen oldiofclose oldfileops	\
 ifeq ($(run-built-tests),yes)
 tests-special += $(objpfx)test-freopen.out $(objpfx)test-fmemopen-mem.out \
 		 $(objpfx)tst-bz22415-mem.out \
+		 $(objpfx)tst-bz24228-mem.out \
 		 $(objpfx)tst-wfile-gconv-mem.out
 ifeq (yes,$(build-shared))
 # Run tst-fopenloc-cmp.out and tst-openloc-mem.out only if shared
@@ -246,6 +251,10 @@  $(objpfx)tst-bz22415-mem.out: $(objpfx)tst-bz22415.out
 	$(common-objpfx)malloc/mtrace $(objpfx)tst-bz22415.mtrace > $@; \
 	$(evaluate-test)
 
+$(objpfx)tst-bz24228-mem.out: $(objpfx)tst-bz24228.out
+	$(common-objpfx)malloc/mtrace $(objpfx)tst-bz24228.mtrace > $@; \
+	$(evaluate-test)
+
 $(objpfx)tst-wfile-gconv-mem.out: $(objpfx)tst-wfile-gconv.out
 	$(common-objpfx)malloc/mtrace $(objpfx)tst-wfile-gconv.mtrace > $@; \
 	$(evaluate-test)
diff --git a/libio/genops.c b/libio/genops.c
index 2a0d9b81df..11a15549e8 100644
--- a/libio/genops.c
+++ b/libio/genops.c
@@ -789,9 +789,16 @@  _IO_unbuffer_all (void)
 
   for (fp = (FILE *) _IO_list_all; fp; fp = fp->_chain)
     {
+      int legacy = 0;
+
+#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1)
+      if (__glibc_unlikely (_IO_vtable_offset (fp) != 0))
+	legacy = 1;
+#endif
+
       if (! (fp->_flags & _IO_UNBUFFERED)
 	  /* Iff stream is un-orientated, it wasn't used. */
-	  && fp->_mode != 0)
+	  && (legacy || fp->_mode != 0))
 	{
 #ifdef _IO_MTSAFE_IO
 	  int cnt;
@@ -805,7 +812,7 @@  _IO_unbuffer_all (void)
 	      __sched_yield ();
 #endif
 
-	  if (! dealloc_buffers && !(fp->_flags & _IO_USER_BUF))
+	  if (! legacy && ! dealloc_buffers && !(fp->_flags & _IO_USER_BUF))
 	    {
 	      fp->_flags |= _IO_USER_BUF;
 
@@ -816,7 +823,7 @@  _IO_unbuffer_all (void)
 
 	  _IO_SETBUF (fp, NULL, 0);
 
-	  if (fp->_mode > 0)
+	  if (! legacy && fp->_mode > 0)
 	    _IO_wsetb (fp, NULL, NULL, 0);
 
 #ifdef _IO_MTSAFE_IO
@@ -827,7 +834,8 @@  _IO_unbuffer_all (void)
 
       /* Make sure that never again the wide char functions can be
 	 used.  */
-      fp->_mode = -1;
+      if (! legacy)
+	fp->_mode = -1;
     }
 
 #ifdef _IO_MTSAFE_IO
diff --git a/libio/tst-bz24228.c b/libio/tst-bz24228.c
new file mode 100644
index 0000000000..6a74500d47
--- /dev/null
+++ b/libio/tst-bz24228.c
@@ -0,0 +1,29 @@ 
+/* BZ #24228 check for memory corruption in legacy libio
+   Copyright (C) 2019 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 <mcheck.h>
+#include <support/test-driver.h>
+
+static int
+do_test (void)
+{
+  mtrace ();
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/libio/tst-bz24228.map b/libio/tst-bz24228.map
new file mode 100644
index 0000000000..4383e0817d
--- /dev/null
+++ b/libio/tst-bz24228.map
@@ -0,0 +1,5 @@ 
+# Hide the symbol from libc.so.6 to switch to the libio/oldfileops.c
+# implementation when it is available for the architecture.
+{
+  local: _IO_stdin_used;
+};