aboutsummaryrefslogtreecommitdiff
path: root/libio/tst-bz22415.c
diff options
context:
space:
mode:
authorAdhemerval Zanella <adhemerval.zanella@linaro.org>2017-11-22 18:33:15 -0200
committerAdhemerval Zanella <adhemerval.zanella@linaro.org>2017-12-12 17:29:54 -0200
commitcc683f7ed4a5bd8ce2c9b715581de727b04eb599 (patch)
treecfd96d62028f35e6477284320fcdb16ce5e061b9 /libio/tst-bz22415.c
parentc80acdc3254cd4801c7605cf468ec137d9ee2d83 (diff)
downloadglibc-cc683f7ed4a5bd8ce2c9b715581de727b04eb599.tar
glibc-cc683f7ed4a5bd8ce2c9b715581de727b04eb599.tar.gz
glibc-cc683f7ed4a5bd8ce2c9b715581de727b04eb599.tar.bz2
glibc-cc683f7ed4a5bd8ce2c9b715581de727b04eb599.zip
libio: Free backup area when it not required (BZ#22415)
Some libio operations fail to correctly free the backup area (created by _IO_{w}default_pbackfail on unget{w}c) resulting in either invalid buffer free operations or memory leaks. For instance, on the example provided by BZ#22415 a following fputc after a fseek to rewind the stream issues an invalid free on the buffer. It is because although _IO_file_overflow correctly (from fputc) correctly calls _IO_free_backup_area, the _IO_new_file_seekoff (called by fseek) updates the FILE internal pointers without first free the backup area (resulting in invalid values in the internal pointers). The wide version also shows an issue, but instead of accessing invalid pointers it leaks the backup memory on fseek/fputwc operation. Checked on x86_64-linux-gnu and i686-linux-gnu. * libio/Makefile (tests): Add tst-bz22415. (tst-bz22415-ENV): New rule. (generated): Add tst-bz22415.mtrace and tst-bz22415.check. (tests-special): Add tst-bz22415-mem.out. ($(objpfx)tst-bz22415-mem.out): New rule. * libio/fileops.c (_IO_new_file_seekoff): Call _IO_free_backup_area in case of a successful seek operation. * libio/wfileops.c (_IO_wfile_seekoff): Likewise. (_IO_wfile_overflow): Call _IO_free_wbackup_area in case a write buffer is required. * libio/tst-bz22415.c: New test.
Diffstat (limited to 'libio/tst-bz22415.c')
-rw-r--r--libio/tst-bz22415.c97
1 files changed, 97 insertions, 0 deletions
diff --git a/libio/tst-bz22415.c b/libio/tst-bz22415.c
new file mode 100644
index 0000000000..d7b23fefb8
--- /dev/null
+++ b/libio/tst-bz22415.c
@@ -0,0 +1,97 @@
+/* Check static buffer handling with setvbuf (BZ #22415)
+
+ Copyright (C) 2017 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <wchar.h>
+#include <mcheck.h>
+
+#include <support/check.h>
+#include <support/temp_file.h>
+
+static int
+do_test (void)
+{
+ mtrace ();
+
+ char *temp_file;
+ TEST_VERIFY_EXIT (create_temp_file ("tst-bz22145.", &temp_file));
+
+ char buf[BUFSIZ];
+
+ {
+ /* Check if backup buffer is correctly freed and changing back
+ to normal buffer does not trigger an invalid free in case of
+ static buffer set by setvbuf. */
+
+ FILE *f = fopen (temp_file, "w+b");
+ TEST_VERIFY_EXIT (f != NULL);
+
+ TEST_VERIFY_EXIT (setvbuf (f, buf, _IOFBF, BUFSIZ) == 0);
+ TEST_VERIFY_EXIT (ungetc ('x', f) == 'x');
+ TEST_VERIFY_EXIT (fseek (f, 0L, SEEK_SET) == 0);
+ TEST_VERIFY_EXIT (fputc ('y', f) == 'y');
+
+ TEST_VERIFY_EXIT (fclose (f) == 0);
+ }
+
+ {
+ /* Check if backup buffer is correctly freed and changing back
+ to normal buffer does not trigger an invalid free in case of
+ static buffer set by setvbuf. */
+
+ FILE *f = fopen (temp_file, "w+b");
+ TEST_VERIFY_EXIT (f != NULL);
+
+ TEST_VERIFY_EXIT (setvbuf (f, buf, _IOFBF, BUFSIZ) == 0);
+ TEST_VERIFY_EXIT (ungetc ('x', f) == 'x');
+ TEST_VERIFY_EXIT (fputc ('y', f) == 'y');
+
+ TEST_VERIFY_EXIT (fclose (f) == 0);
+ }
+
+ {
+ FILE *f = fopen (temp_file, "w+b");
+ TEST_VERIFY_EXIT (f != NULL);
+
+ TEST_VERIFY_EXIT (setvbuf (f, buf, _IOFBF, BUFSIZ) == 0);
+ TEST_VERIFY_EXIT (ungetwc (L'x', f) == L'x');
+ TEST_VERIFY_EXIT (fseek (f, 0L, SEEK_SET) == 0);
+ TEST_VERIFY_EXIT (fputwc (L'y', f) == L'y');
+
+ TEST_VERIFY_EXIT (fclose (f) == 0);
+ }
+
+ {
+ FILE *f = fopen (temp_file, "w+b");
+ TEST_VERIFY_EXIT (f != NULL);
+
+ TEST_VERIFY_EXIT (setvbuf (f, buf, _IOFBF, BUFSIZ) == 0);
+ TEST_VERIFY_EXIT (ungetwc (L'x', f) == L'x');
+ TEST_VERIFY_EXIT (fputwc (L'y', f) == L'y');
+
+ TEST_VERIFY_EXIT (fclose (f) == 0);
+ }
+
+ free (temp_file);
+
+ return 0;
+}
+
+#include <support/test-driver.c>