vfprintf: Avoid alloca for user-defined format specifier arguments
Commit Message
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.
@@ -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;
}