Patchwork vfprintf: Avoid alloca for user-defined format specifier arguments

login
register
mail settings
Submitter Florian Weimer
Date June 19, 2017, 4:22 p.m.
Message ID <20170619162242.63523402AEC3C@oldenburg.str.redhat.com>
Download mbox | patch
Permalink /patch/21108/
State New
Headers show

Comments

Florian Weimer - June 19, 2017, 4:22 p.m.
2017-06-19  Florian Weimer  <fweimer@redhat.com>

	* stdio-common/vfprintf.c (struct spec_data_args): Define using
	dynarray.
	(printf_positional): Use struct spec_data_args instead of alloca.

Patch

diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c
index d58f0ea..0a5e541 100644
--- a/stdio-common/vfprintf.c
+++ b/stdio-common/vfprintf.c
@@ -1680,6 +1680,15 @@  allocate_user_args_buffer (size_t nargs, const int *args_size,
   return malloc (size);
 }
 
+/* Array for storing argument pointers for user-defined format
+   specifiers.  Most user-defined specifiers use a single argument
+   only, so we override the initial allocation size.  */
+#define DYNARRAY_STRUCT spec_data_args
+#define DYNARRAY_ELEMENT const void *
+#define DYNARRAY_PREFIX spec_data_args_
+#define DYNARRAY_INITIAL_SIZE 2
+#include <malloc/dynarray-skeleton.c>
+
 static int
 printf_positional (_IO_FILE *s, const CHAR_T *format, int readonly_format,
 		   va_list ap, va_list *ap_savep, int done, int nspecs_done,
@@ -1693,6 +1702,11 @@  printf_positional (_IO_FILE *s, const CHAR_T *format, int readonly_format,
   struct printf_spec *specs = specsbuf.data;
   size_t specs_limit = specsbuf.length / sizeof (specs[0]);
 
+  /* Pointer argument array, for passing to format specifier
+     callbacks.  */
+  struct spec_data_args spec_data_args;
+  spec_data_args_init (&spec_data_args);
+
   /* Used as a backing store for args_value, args_size, args_type
      below.  */
   struct scratch_buffer argsbuf;
@@ -1979,17 +1993,24 @@  printf_positional (_IO_FILE *s, const CHAR_T *format, int readonly_format,
 	      && __printf_function_table != NULL
 	      && __printf_function_table[(size_t) spec] != NULL)
 	    {
-	      const void **ptr = alloca (specs[nspecs_done].ndata_args
-					 * sizeof (const void *));
+	      if (!spec_data_args_resize (&spec_data_args,
+					  specs[nspecs_done].ndata_args))
+		  {
+		    __set_errno (ENOMEM);
+		    done = -1;
+		    goto all_done;
+		  }
 
 	      /* Fill in an array of pointers to the argument values.  */
 	      for (unsigned int i = 0; i < specs[nspecs_done].ndata_args;
 		   ++i)
-		ptr[i] = &args_value[specs[nspecs_done].data_arg + i];
+		*spec_data_args_at (&spec_data_args, i) =
+		  &args_value[specs[nspecs_done].data_arg + i];
 
 	      /* Call the function.  */
 	      function_done = __printf_function_table[(size_t) spec]
-		(s, &specs[nspecs_done].info, ptr);
+		(s, &specs[nspecs_done].info,
+		 spec_data_args_begin (&spec_data_args));
 
 	      if (function_done != -2)
 		{
@@ -2051,6 +2072,7 @@  printf_positional (_IO_FILE *s, const CHAR_T *format, int readonly_format,
   free (user_args_buffer);
   scratch_buffer_free (&argsbuf);
   scratch_buffer_free (&specsbuf);
+  spec_data_args_free (&spec_data_args);
   return done;
 }