changeset 4872:c5afc99b8ce5

Use xsize.h to protect against memory size overflows.
author Bruno Haible <bruno@clisp.org>
date Mon, 17 Nov 2003 15:14:21 +0000
parents 87df35000dab
children 8b1923c943a6
files ChangeLog lib/ChangeLog lib/printf-args.c lib/printf-args.h lib/printf-parse.c lib/printf-parse.h lib/vasnprintf.c m4/ChangeLog m4/vasnprintf.m4 modules/vasnprintf
diffstat 10 files changed, 201 insertions(+), 89 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2003-11-17  Bruno Haible  <bruno@clisp.org>
+
+	* modules/vasnprintf (Files): Add m4/ssize_t.m4.
+	(Depends-on): Add xsize.
+
 2003-11-12  Paul Eggert  <eggert@twinsun.com>
 
 	* modules/xalloc (Files): Undo latest change, since xalloc.h
--- a/lib/ChangeLog
+++ b/lib/ChangeLog
@@ -1,3 +1,38 @@
+2003-11-16  Paul Eggert  <eggert@twinsun.com>
+            Bruno Haible  <bruno@clisp.org>
+
+	Protect against address arithmetic overflow.
+	* printf-args.h: Include stddef.h.
+	(arguments): Change type of field 'count' to size_t.
+	* printf-args.c (printf_fetchargs): Use size_t instead of
+	'unsigned int' where appropriate.
+	* printf-parse.h: Include sys/types.h.
+	(char_directive): Change type of *arg_index fields to ssize_t.
+	(char_directives): Change type of fields 'count', max_*_length to
+	size_t.
+	* printf-parse.c: Include sys/types.h and xsize.h.
+	(SSIZE_MAX): Define fallback value.
+	(PRINTF_PARSE): Use size_t instead of 'unsigned int' and ssize_t
+	instead of 'int' where appropriate. Check a_allocated, d_allocated
+	against overflow. Reject %m$ argument numbers > SSIZE_MAX + 1.
+	* vasnprintf.c: Include xsize.h.
+	(VASNPRINTF): Use size_t instead of 'unsigned int' where appropriate.
+	Check alloca, malloc, realloc, ENSURE_ALLOCATION arguments against
+	overflow. Avoid wraparound when converting a width or precision from
+	decimal to binary.
+
+2003-11-16  Bruno Haible  <bruno@clisp.org>
+
+	Update from GNU gettext.
+	* printf-parse.c: Generalize to it can be compiled for wide strings.
+	(PRINTF_PARSE, CHAR_T, DIRECTIVE, DIRECTIVES): New macros.
+	* vasnprintf.c: Generalize to it can be compiled for wide strings.
+	(VASNPRINTF, CHAR_T, DIRECTIVE, DIRECTIVES, PRINTF_PARSE, USE_SNPRINTF,
+	SNPRINTF): New macros.
+	Don't include <alloca.h> if the file is used inside libintl.
+	(local_wcslen): New function, for Solaris 2.5.1.
+	(VASNPRINTF): Use it instead of wcslen.
+
 2003-11-16  Bruno Haible  <bruno@clisp.org>
 
 	* xsize.h (xmax): New function.
--- a/lib/printf-args.c
+++ b/lib/printf-args.c
@@ -28,7 +28,7 @@
 int
 printf_fetchargs (va_list args, arguments *a)
 {
-  unsigned int i;
+  size_t i;
   argument *ap;
 
   for (i = 0, ap = &a->arg[0]; i < a->count; i++, ap++)
--- a/lib/printf-args.h
+++ b/lib/printf-args.h
@@ -18,6 +18,9 @@
 #ifndef _PRINTF_ARGS_H
 #define _PRINTF_ARGS_H
 
+/* Get size_t.  */
+#include <stddef.h>
+
 /* Get wchar_t.  */
 #ifdef HAVE_WCHAR_T
 # include <stddef.h>
@@ -116,7 +119,7 @@
 
 typedef struct
 {
-  unsigned int count;
+  size_t count;
   argument *arg;
 }
 arguments;
--- a/lib/printf-parse.c
+++ b/lib/printf-parse.c
@@ -29,6 +29,9 @@
 /* Get size_t, NULL.  */
 #include <stddef.h>
 
+/* Get ssize_t.  */
+#include <sys/types.h>
+
 /* Get intmax_t.  */
 #if HAVE_STDINT_H_WITH_UINTMAX
 # include <stdint.h>
@@ -40,6 +43,13 @@
 /* malloc(), realloc(), free().  */
 #include <stdlib.h>
 
+/* Checked size_t computations.  */
+#include "xsize.h"
+
+#ifndef SSIZE_MAX
+# define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2))
+#endif
+
 #if WIDE_CHAR_VERSION
 # define PRINTF_PARSE wprintf_parse
 # define CHAR_T wchar_t
@@ -59,11 +69,11 @@
 PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a)
 {
   const CHAR_T *cp = format;		/* pointer into format */
-  int arg_posn = 0;		/* number of regular arguments consumed */
-  unsigned int d_allocated;		/* allocated elements of d->dir */
-  unsigned int a_allocated;		/* allocated elements of a->arg */
-  unsigned int max_width_length = 0;
-  unsigned int max_precision_length = 0;
+  ssize_t arg_posn = 0;		/* number of regular arguments consumed */
+  size_t d_allocated;			/* allocated elements of d->dir */
+  size_t a_allocated;			/* allocated elements of a->arg */
+  size_t max_width_length = 0;
+  size_t max_precision_length = 0;
 
   d->count = 0;
   d_allocated = 1;
@@ -78,16 +88,22 @@
 
 #define REGISTER_ARG(_index_,_type_) \
   {									\
-    unsigned int n = (_index_);						\
+    size_t n = (_index_);						\
     if (n >= a_allocated)						\
       {									\
+	size_t memory_size;						\
 	argument *memory;						\
-	a_allocated = 2 * a_allocated;					\
+									\
+	a_allocated = xtimes (a_allocated, 2);				\
 	if (a_allocated <= n)						\
-	  a_allocated = n + 1;						\
+	  a_allocated = xsum (n, 1);					\
+	memory_size = xtimes (a_allocated, sizeof (argument));		\
+	if (size_overflow_p (memory_size))				\
+	  /* Overflow, would lead to out of memory.  */			\
+	  goto error;							\
 	memory = (a->arg						\
-		  ? realloc (a->arg, a_allocated * sizeof (argument))	\
-		  : malloc (a_allocated * sizeof (argument)));		\
+		  ? realloc (a->arg, memory_size)			\
+		  : malloc (memory_size));				\
 	if (memory == NULL)						\
 	  /* Out of memory.  */						\
 	  goto error;							\
@@ -107,7 +123,7 @@
       CHAR_T c = *cp++;
       if (c == '%')
 	{
-	  int arg_index = -1;
+	  ssize_t arg_index = -1;
 	  DIRECTIVE *dp = &d->dir[d->count];/* pointer to next directive */
 
 	  /* Initialize the next directive.  */
@@ -130,13 +146,16 @@
 		;
 	      if (*np == '$')
 		{
-		  unsigned int n = 0;
+		  size_t n = 0;
 
 		  for (np = cp; *np >= '0' && *np <= '9'; np++)
-		    n = 10 * n + (*np - '0');
+		    n = xsum (xtimes (n, 10), *np - '0');
 		  if (n == 0)
 		    /* Positional argument 0.  */
 		    goto error;
+		  if (size_overflow_p (n) || n - 1 > SSIZE_MAX)
+		    /* n too large, would lead to out of memory later.  */
+		    goto error;
 		  arg_index = n - 1;
 		  cp = np + 1;
 		}
@@ -197,24 +216,32 @@
 		    ;
 		  if (*np == '$')
 		    {
-		      unsigned int n = 0;
+		      size_t n = 0;
 
 		      for (np = cp; *np >= '0' && *np <= '9'; np++)
-			n = 10 * n + (*np - '0');
+			n = xsum (xtimes (n, 10), *np - '0');
 		      if (n == 0)
 			/* Positional argument 0.  */
 			goto error;
+		      if (size_overflow_p (n) || n - 1 > SSIZE_MAX)
+			/* n too large, would lead to out of memory later.  */
+			goto error;
 		      dp->width_arg_index = n - 1;
 		      cp = np + 1;
 		    }
 		}
 	      if (dp->width_arg_index < 0)
-		dp->width_arg_index = arg_posn++;
+		{
+		  dp->width_arg_index = arg_posn++;
+		  if (dp->width_arg_index < 0)
+		    /* arg_posn wrapped around at SSIZE_MAX.  */
+		    goto error;
+		}
 	      REGISTER_ARG (dp->width_arg_index, TYPE_INT);
 	    }
 	  else if (*cp >= '0' && *cp <= '9')
 	    {
-	      unsigned int width_length;
+	      size_t width_length;
 
 	      dp->width_start = cp;
 	      for (; *cp >= '0' && *cp <= '9'; cp++)
@@ -246,24 +273,33 @@
 			;
 		      if (*np == '$')
 			{
-			  unsigned int n = 0;
+			  size_t n = 0;
 
 			  for (np = cp; *np >= '0' && *np <= '9'; np++)
-			    n = 10 * n + (*np - '0');
+			    n = xsum (xtimes (n, 10), *np - '0');
 			  if (n == 0)
 			    /* Positional argument 0.  */
 			    goto error;
+			  if (size_overflow_p (n) || n - 1 > SSIZE_MAX)
+			    /* n too large, would lead to out of memory
+			       later.  */
+			    goto error;
 			  dp->precision_arg_index = n - 1;
 			  cp = np + 1;
 			}
 		    }
 		  if (dp->precision_arg_index < 0)
-		    dp->precision_arg_index = arg_posn++;
+		    {
+		      dp->precision_arg_index = arg_posn++;
+		      if (dp->precision_arg_index < 0)
+			/* arg_posn wrapped around at SSIZE_MAX.  */
+			goto error;
+		    }
 		  REGISTER_ARG (dp->precision_arg_index, TYPE_INT);
 		}
 	      else
 		{
-		  unsigned int precision_length;
+		  size_t precision_length;
 
 		  dp->precision_start = cp - 1;
 		  for (; *cp >= '0' && *cp <= '9'; cp++)
@@ -456,7 +492,12 @@
 	      {
 		dp->arg_index = arg_index;
 		if (dp->arg_index < 0)
-		  dp->arg_index = arg_posn++;
+		  {
+		    dp->arg_index = arg_posn++;
+		    if (dp->arg_index < 0)
+		      /* arg_posn wrapped around at SSIZE_MAX.  */
+		      goto error;
+		  }
 		REGISTER_ARG (dp->arg_index, type);
 	      }
 	    dp->conversion = c;
@@ -466,10 +507,18 @@
 	  d->count++;
 	  if (d->count >= d_allocated)
 	    {
+	      size_t memory_size;
 	      DIRECTIVE *memory;
 
-	      d_allocated = 2 * d_allocated;
-	      memory = realloc (d->dir, d_allocated * sizeof (DIRECTIVE));
+	      d_allocated = xtimes (d_allocated, 2);
+	      if (size_overflow_p (d_allocated))
+		/* Overflow, would lead to out of memory.  */
+		goto error;
+	      memory_size = xtimes (d_allocated, sizeof (DIRECTIVE));
+	      if (size_overflow_p (memory_size))
+		/* Overflow, would lead to out of memory.  */
+		goto error;
+	      memory = realloc (d->dir, memory_size);
 	      if (memory == NULL)
 		/* Out of memory.  */
 		goto error;
--- a/lib/printf-parse.h
+++ b/lib/printf-parse.h
@@ -1,5 +1,5 @@
 /* Parse printf format string.
-   Copyright (C) 1999, 2002 Free Software Foundation, Inc.
+   Copyright (C) 1999, 2002-2003 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -20,6 +20,9 @@
 
 #include "printf-args.h"
 
+/* Get ssize_t.  */
+#include <sys/types.h>
+
 
 /* Flags */
 #define FLAG_GROUP	 1	/* ' flag */
@@ -37,22 +40,22 @@
   int flags;
   const char* width_start;
   const char* width_end;
-  int width_arg_index;
+  ssize_t width_arg_index;
   const char* precision_start;
   const char* precision_end;
-  int precision_arg_index;
+  ssize_t precision_arg_index;
   char conversion; /* d i o u x X f e E g G c s p n U % but not C S */
-  int arg_index;
+  ssize_t arg_index;
 }
 char_directive;
 
 /* A parsed format string.  */
 typedef struct
 {
-  unsigned int count;
+  size_t count;
   char_directive *dir;
-  unsigned int max_width_length;
-  unsigned int max_precision_length;
+  size_t max_width_length;
+  size_t max_precision_length;
 }
 char_directives;
 
--- a/lib/vasnprintf.c
+++ b/lib/vasnprintf.c
@@ -48,6 +48,9 @@
 # include "printf-parse.h"
 #endif
 
+/* Checked size_t computations.  */
+#include "xsize.h"
+
 /* For those losing systems which don't have 'alloca' we have to add
    some additional code emulating it.  */
 #ifdef HAVE_ALLOCA
@@ -136,11 +139,12 @@
     }
 
   {
+    size_t buf_neededlength =
+      xsum4 (7, d.max_width_length, d.max_precision_length, 6);
     CHAR_T *buf =
-      (CHAR_T *) alloca ((7 + d.max_width_length + d.max_precision_length + 6)
-			 * sizeof (CHAR_T));
+      (CHAR_T *) alloca (xtimes (buf_neededlength, sizeof (CHAR_T)));
     const CHAR_T *cp;
-    unsigned int i;
+    size_t i;
     DIRECTIVE *dp;
     /* Output string accumulator.  */
     CHAR_T *result;
@@ -162,28 +166,26 @@
        result is either == resultbuf or == NULL or malloc-allocated.
        If length > 0, then result != NULL.  */
 
+    /* Ensures that allocated >= needed.  Aborts through a jump to
+       out_of_memory if needed is SIZE_MAX or otherwise too big.  */
 #define ENSURE_ALLOCATION(needed) \
     if ((needed) > allocated)						     \
       {									     \
+	size_t memory_size;						     \
 	CHAR_T *memory;							     \
 									     \
-	allocated = (allocated > 0 ? 2 * allocated : 12);		     \
+	allocated = (allocated > 0 ? xtimes (allocated, 2) : 12);	     \
 	if ((needed) > allocated)					     \
 	  allocated = (needed);						     \
+	memory_size = xtimes (allocated, sizeof (CHAR_T));		     \
+	if (size_overflow_p (memory_size))				     \
+	  goto out_of_memory;						     \
 	if (result == resultbuf || result == NULL)			     \
-	  memory = (CHAR_T *) malloc (allocated * sizeof (CHAR_T));	     \
+	  memory = (CHAR_T *) malloc (memory_size);			     \
 	else								     \
-	  memory = (CHAR_T *) realloc (result, allocated * sizeof (CHAR_T)); \
-									     \
+	  memory = (CHAR_T *) realloc (result, memory_size);		     \
 	if (memory == NULL)						     \
-	  {								     \
-	    if (!(result == resultbuf || result == NULL))		     \
-	      free (result);						     \
-	    freea (buf);						     \
-	    CLEANUP ();							     \
-	    errno = ENOMEM;						     \
-	    return NULL;						     \
-	  }								     \
+	  goto out_of_memory;						     \
 	if (result == resultbuf && length > 0)				     \
 	  memcpy (memory, result, length * sizeof (CHAR_T));		     \
 	result = memory;						     \
@@ -194,10 +196,11 @@
 	if (cp != dp->dir_start)
 	  {
 	    size_t n = dp->dir_start - cp;
+	    size_t augmented_length = xsum (length, n);
 
-	    ENSURE_ALLOCATION (length + n);
+	    ENSURE_ALLOCATION (augmented_length);
 	    memcpy (result + length, cp, n * sizeof (CHAR_T));
-	    length += n;
+	    length = augmented_length;
 	  }
 	if (i == d.count)
 	  break;
@@ -205,11 +208,14 @@
 	/* Execute a single directive.  */
 	if (dp->conversion == '%')
 	  {
+	    size_t augmented_length;
+
 	    if (!(dp->arg_index < 0))
 	      abort ();
-	    ENSURE_ALLOCATION (length + 1);
+	    augmented_length = xsum (length, 1);
+	    ENSURE_ALLOCATION (augmented_length);
 	    result[length] = '%';
-	    length += 1;
+	    length = augmented_length;
 	  }
 	else
 	  {
@@ -248,15 +254,15 @@
 		unsigned int prefix_count;
 		int prefixes[2];
 #if !USE_SNPRINTF
-		unsigned int tmp_length;
+		size_t tmp_length;
 		CHAR_T tmpbuf[700];
 		CHAR_T *tmp;
 
 		/* Allocate a temporary buffer of sufficient size for calling
 		   sprintf.  */
 		{
-		  unsigned int width;
-		  unsigned int precision;
+		  size_t width;
+		  size_t precision;
 
 		  width = 0;
 		  if (dp->width_start != dp->width_end)
@@ -268,14 +274,14 @@
 			  if (!(a.arg[dp->width_arg_index].type == TYPE_INT))
 			    abort ();
 			  arg = a.arg[dp->width_arg_index].a.a_int;
-			  width = (arg < 0 ? -arg : arg);
+			  width = (arg < 0 ? (unsigned int) (-arg) : arg);
 			}
 		      else
 			{
 			  const CHAR_T *digitp = dp->width_start;
 
 			  do
-			    width = width * 10 + (*digitp++ - '0');
+			    width = xsum (xtimes (width, 10), *digitp++ - '0');
 			  while (digitp != dp->width_end);
 			}
 		    }
@@ -298,7 +304,7 @@
 
 			  precision = 0;
 			  do
-			    precision = precision * 10 + (*digitp++ - '0');
+			    precision = xsum (xtimes (precision, 10), *digitp++ - '0');
 			  while (digitp != dp->precision_end);
 			}
 		    }
@@ -399,7 +405,6 @@
 					  * 2 /* estimate for FLAG_GROUP */
 					 )
 			  + 1 /* turn floor into ceil */
-			  + precision
 			  + 10; /* sign, decimal point etc. */
 		      else
 # endif
@@ -409,15 +414,15 @@
 					  * 2 /* estimate for FLAG_GROUP */
 					 )
 			  + 1 /* turn floor into ceil */
-			  + precision
 			  + 10; /* sign, decimal point etc. */
+		      tmp_length = xsum (tmp_length, precision);
 		      break;
 
 		    case 'e': case 'E': case 'g': case 'G':
 		    case 'a': case 'A':
 		      tmp_length =
-			precision
-			+ 12; /* sign, decimal point, exponent etc. */
+			12; /* sign, decimal point, exponent etc. */
+		      tmp_length = xsum (tmp_length, precision);
 		      break;
 
 		    case 'c':
@@ -432,14 +437,14 @@
 		    case 's':
 # ifdef HAVE_WCHAR_T
 		      if (type == TYPE_WIDE_STRING)
-#  if WIDE_CHAR_VERSION
-			tmp_length =
-			  local_wcslen (a.arg[dp->arg_index].a.a_wide_string);
-#  else
-			tmp_length =
-			  local_wcslen (a.arg[dp->arg_index].a.a_wide_string)
-			  * MB_CUR_MAX;
+			{
+			  tmp_length =
+			    local_wcslen (a.arg[dp->arg_index].a.a_wide_string);
+
+#  if !WIDE_CHAR_VERSION
+			  tmp_length = xtimes (tmp_length, MB_CUR_MAX);
 #  endif
+			}
 		      else
 # endif
 			tmp_length = strlen (a.arg[dp->arg_index].a.a_string);
@@ -461,24 +466,22 @@
 		  if (tmp_length < width)
 		    tmp_length = width;
 
-		  tmp_length++; /* account for trailing NUL */
+		  tmp_length = xsum (tmp_length, 1); /* account for trailing NUL */
 		}
 
 		if (tmp_length <= sizeof (tmpbuf) / sizeof (CHAR_T))
 		  tmp = tmpbuf;
 		else
 		  {
-		    tmp = (CHAR_T *) malloc (tmp_length * sizeof (CHAR_T));
+		    size_t tmp_memsize = xtimes (tmp_length, sizeof (CHAR_T));
+
+		    if (size_overflow_p (tmp_memsize))
+		      /* Overflow, would lead to out of memory.  */
+		      goto out_of_memory;
+		    tmp = (CHAR_T *) malloc (tmp_memsize);
 		    if (tmp == NULL)
-		      {
-			/* Out of memory.  */
-			if (!(result == resultbuf || result == NULL))
-			  free (result);
-			freea (buf);
-			CLEANUP ();
-			errno = ENOMEM;
-			return NULL;
-		      }
+		      /* Out of memory.  */
+		      goto out_of_memory;
 		  }
 #endif
 
@@ -564,7 +567,7 @@
 #if USE_SNPRINTF
 		/* Prepare checking whether snprintf returns the count
 		   via %n.  */
-		ENSURE_ALLOCATION (length + 1);
+		ENSURE_ALLOCATION (xsum (length, 1));
 		result[length] = '\0';
 #endif
 
@@ -769,7 +772,8 @@
 				   *and* it returns -1 (rather than the length
 				   that would have been required) when the
 				   buffer is too small.  */
-				size_t bigger_need = 2 * allocated + 12;
+				size_t bigger_need =
+				  xsum (xtimes (allocated, 2), 12);
 				ENSURE_ALLOCATION (bigger_need);
 				continue;
 			      }
@@ -803,10 +807,8 @@
 			/* Need at least count bytes.  But allocate
 			   proportionally, to avoid looping eternally if
 			   snprintf() reports a too small count.  */
-			size_t n = length + count;
-
-			if (n < 2 * allocated)
-			  n = 2 * allocated;
+			size_t n =
+			  xmax (xsum (length, count), xtimes (allocated, 2));
 
 			ENSURE_ALLOCATION (n);
 #if USE_SNPRINTF
@@ -831,7 +833,7 @@
       }
 
     /* Add the final NUL.  */
-    ENSURE_ALLOCATION (length + 1);
+    ENSURE_ALLOCATION (xsum (length, 1));
     result[length] = '\0';
 
     if (result != resultbuf && length + 1 < allocated)
@@ -848,6 +850,14 @@
     CLEANUP ();
     *lengthp = length;
     return result;
+
+  out_of_memory:
+    if (!(result == resultbuf || result == NULL))
+      free (result);
+    freea (buf);
+    CLEANUP ();
+    errno = ENOMEM;
+    return NULL;
   }
 }
 
--- a/m4/ChangeLog
+++ b/m4/ChangeLog
@@ -1,3 +1,7 @@
+2003-11-17  Bruno Haible  <bruno@clisp.org>
+
+	* vasnprintf.m4 (gl_PREREQ_PRINTF_PARSE): Require gt_TYPE_SSIZE_T.
+
 2003-11-12  Paul Eggert  <eggert@twinsun.com>
 
 	* xalloc.m4 (gl_PREREQ_XALLOC): Do not require gl_SIZE_MAX or
@@ -8,7 +12,7 @@
 	* size_max.m4: New file.
 	* ptrdiff_max.m4: New file.
 	* xsize,m4 (gl_XSIZE): Require gl_SIZE_MAX.
-	* xalloc.m4 (gl_PREREQ_XALLOC): New file.
+	* xalloc.m4 (gl_PREREQ_XALLOC): New macro.
 	(gl_XALLOC): Invoke it.
 
 2003-11-04  Bruno Haible  <bruno@clisp.org>
--- a/m4/vasnprintf.m4
+++ b/m4/vasnprintf.m4
@@ -1,4 +1,4 @@
-# vasnprintf.m4 serial 2
+# vasnprintf.m4 serial 3
 dnl Copyright (C) 2002-2003 Free Software Foundation, Inc.
 dnl This file is free software, distributed under the terms of the GNU
 dnl General Public License.  As a special exception to the GNU General
@@ -38,6 +38,7 @@
   AC_REQUIRE([gt_TYPE_WCHAR_T])
   AC_REQUIRE([gt_TYPE_WINT_T])
   AC_REQUIRE([AC_TYPE_SIZE_T])
+  AC_REQUIRE([gt_TYPE_SSIZE_T])
   AC_CHECK_TYPES(ptrdiff_t)
   AC_REQUIRE([gt_AC_TYPE_INTMAX_T])
 ])
--- a/modules/vasnprintf
+++ b/modules/vasnprintf
@@ -15,10 +15,12 @@
 m4/wint_t.m4
 m4/longlong.m4
 m4/intmax_t.m4
+m4/ssize_t.m4
 m4/vasnprintf.m4
 
 Depends-on:
 alloca
+xsize
 
 configure.ac:
 gl_FUNC_VASNPRINTF