about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPaul Pluzhnikov <ppluzhnikov@google.com>2015-09-16 09:16:11 -0700
committerPaul Pluzhnikov <ppluzhnikov@google.com>2015-09-16 09:16:11 -0700
commit560b04462f899e76a0062ec89422caa6e94fd67f (patch)
tree00c0a46a7eb71c9443bbe77bd2dcff14c091e9cf
parent1f60740e5f03823a557d1543cf08880a11d719de (diff)
downloadglibc-560b04462f899e76a0062ec89422caa6e94fd67f.tar.gz
glibc-560b04462f899e76a0062ec89422caa6e94fd67f.tar.xz
glibc-560b04462f899e76a0062ec89422caa6e94fd67f.zip
Fix BZ #18872 -- memory leak in printf_positional.
-rw-r--r--ChangeLog9
-rw-r--r--NEWS2
-rw-r--r--stdio-common/Makefile17
-rw-r--r--stdio-common/tst-printf-bz18872.sh70
-rw-r--r--stdio-common/vfprintf.c4
5 files changed, 100 insertions, 2 deletions
diff --git a/ChangeLog b/ChangeLog
index 9e83d22841..df355abe92 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2015-09-16  Paul Eggert  <eggert@cs.ucla.edu>
+	    Paul Pluzhnikov  <ppluzhnikov@google.com>
+
+	[BZ #18872]
+	* stdio-common/Makefile (tst-printf-bz18872): New test.
+	(tst-printf-bz18872-mem.out): Likewise.
+	* stdio-common/tst-printf-bz18872.sh: Generate new test.
+	* stdio-common/vfprintf.c: Fix memory leaks.
+
 2015-09-16  Andreas Schwab  <schwab@suse.de>
 
 	[BZ #17244]
diff --git a/NEWS b/NEWS
index 65f69c579d..d46717e47a 100644
--- a/NEWS
+++ b/NEWS
@@ -14,7 +14,7 @@ Version 2.23
   18084, 18086, 18240, 18265, 18370, 18421, 18480, 18525, 18595, 18610,
   18618, 18647, 18661, 18674, 18675, 18681, 18757, 18778, 18781, 18787,
   18789, 18790, 18795, 18796, 18820, 18823, 18824, 18857, 18863, 18870,
-  18873, 18875, 18887, 18921, 18952, 18961, 18966, 18967.
+  18872, 18873, 18875, 18887, 18921, 18952, 18961, 18966, 18967.
 
 * The obsolete header <regexp.h> has been removed.  Programs that require
   this header must be updated to use <regex.h> instead.
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index d0bf0e1083..40f5cdf267 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -57,17 +57,23 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \
 	 bug19 bug19a tst-popen2 scanf13 scanf14 scanf15 bug20 bug21 bug22 \
 	 scanf16 scanf17 tst-setvbuf1 tst-grouping bug23 bug24 \
 	 bug-vfprintf-nargs tst-long-dbl-fphex tst-fphex-wide tst-sprintf3 \
-	 bug25 tst-printf-round bug23-2 bug23-3 bug23-4 bug26 tst-fmemopen3
+	 bug25 tst-printf-round bug23-2 bug23-3 bug23-4 bug26 tst-fmemopen3 \
+	 tst-printf-bz18872
 
 test-srcs = tst-unbputc tst-printf
 
 ifeq ($(run-built-tests),yes)
 tests-special += $(objpfx)tst-unbputc.out $(objpfx)tst-printf.out \
+		 $(objpfx)tst-printf-bz18872-mem.out \
 		 $(objpfx)tst-setvbuf1-cmp.out
+generated += tst-printf-bz18872.c tst-printf-bz18872.mtrace \
+	     tst-printf-bz18872-mem.out
 endif
 
 include ../Rules
 
+tst-printf-bz18872-ENV = MALLOC_TRACE=$(objpfx)tst-printf-bz18872.mtrace
+
 ifeq ($(run-built-tests),yes)
 $(objpfx)tst-unbputc.out: tst-unbputc.sh $(objpfx)tst-unbputc
 	$(SHELL) $< $(common-objpfx) '$(test-program-prefix)'; \
@@ -76,6 +82,15 @@ $(objpfx)tst-unbputc.out: tst-unbputc.sh $(objpfx)tst-unbputc
 $(objpfx)tst-printf.out: tst-printf.sh $(objpfx)tst-printf
 	$(SHELL) $< $(common-objpfx) '$(test-program-prefix)'; \
 	$(evaluate-test)
+
+# We generate this source because it requires a printf invocation with
+# 10K arguments.
+$(objpfx)tst-printf-bz18872.c: tst-printf-bz18872.sh
+	rm -f $@ && $(BASH) $^ > $@.new && mv $@.new $@
+
+$(objpfx)tst-printf-bz18872-mem.out: $(objpfx)tst-printf-bz18872.out
+	$(common-objpfx)malloc/mtrace $(objpfx)tst-printf-bz18872.mtrace > $@; \
+	$(evaluate-test)
 endif
 
 CFLAGS-vfprintf.c = -Wno-uninitialized
diff --git a/stdio-common/tst-printf-bz18872.sh b/stdio-common/tst-printf-bz18872.sh
new file mode 100644
index 0000000000..0127e73024
--- /dev/null
+++ b/stdio-common/tst-printf-bz18872.sh
@@ -0,0 +1,70 @@
+#!/bin/bash
+# Copyright (C) 2015 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/>.
+
+# To test BZ #18872, we need a printf() with 10K arguments.
+# Such a printf could be generated with non-trivial macro
+# application, but it's simpler to generate the test source
+# via this script.
+
+n_args=10000
+
+cat <<'EOF'
+#include <stdio.h>
+#include <mcheck.h>
+
+/*
+  Compile do_test without optimization: GCC 4.9/5.0/6.0 takes a long time
+  to build this source. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67396  */
+#pragma GCC push_options
+#pragma GCC optimize ("-O0")
+
+int do_test (void)
+{
+    mtrace ();
+    printf (
+EOF
+
+for ((j = 0; j < $n_args / 10; j++)); do
+  for ((k = 0; k < 10; k++)); do
+    printf '"%%%d$s" ' $((10 * $j + $k + 1))
+  done
+  printf "\n"
+done
+
+printf '"%%%d$s",\n' $(($n_args + 1))
+
+for ((j = 0; j < $n_args / 10; j++)); do
+  for ((k = 0; k < 10; k++)); do
+    printf '"a", '
+  done
+  printf "  /* %4d */\n" $((10 * $j + $k))
+done
+
+printf '"\\n");'
+
+
+cat <<'EOF'
+
+  return 0;
+}
+#pragma GCC pop_options
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
+
+EOF
diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c
index 4b1bb8feb5..5e408d2961 100644
--- a/stdio-common/vfprintf.c
+++ b/stdio-common/vfprintf.c
@@ -2091,6 +2091,10 @@ printf_positional (_IO_FILE *s, const CHAR_T *format, int readonly_format,
 		 - specs[nspecs_done].end_of_fmt);
     }
  all_done:
+  if (__glibc_unlikely (specs_malloced))
+    free (specs);
+  if (__glibc_unlikely (args_malloced != NULL))
+    free (args_malloced);
   if (__glibc_unlikely (workstart != NULL))
     free (workstart);
   return done;