about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--time/Makefile4
-rw-r--r--time/strftime.c61
-rw-r--r--time/tst-strftime.c74
4 files changed, 128 insertions, 19 deletions
diff --git a/ChangeLog b/ChangeLog
index 86ae48950b..f162670c2b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2003-01-19  Ulrich Drepper  <drepper@redhat.com>
+
+	* time/strftime.c (my_strftime): Handle very large width
+	specifications for numeric values correctly.  Improve checks for
+	overflow.
+	* time/Makefile (tests): Add tst-strftime.
+	* time/tst-strftime.c: New file.
+
 2003-01-18  Ulrich Drepper  <drepper@redhat.com>
 
 	* nis/nss_nis/nis-hosts.c: Make _nss_nis_endhostent an alias of
diff --git a/time/Makefile b/time/Makefile
index 532066b16d..a7fe2f91c5 100644
--- a/time/Makefile
+++ b/time/Makefile
@@ -1,4 +1,4 @@
-# Copyright (C) 1991-1999,2000,01,02 Free Software Foundation, Inc.
+# Copyright (C) 1991-2002, 2003 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
@@ -34,7 +34,7 @@ aux :=	    era alt_digit lc-time-cleanup
 distribute := datemsk
 
 tests	:= test_time clocktest tst-posixtz tst-strptime tst_wcsftime \
-	   tst-getdate tst-mktime tst-ftime_l
+	   tst-getdate tst-mktime tst-ftime_l tst-strftime
 
 include ../Rules
 
diff --git a/time/strftime.c b/time/strftime.c
index 807bb5c4a0..a3256ea245 100644
--- a/time/strftime.c
+++ b/time/strftime.c
@@ -1,4 +1,5 @@
-/* Copyright (C) 1991-1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+/* Copyright (C) 1991-1999, 2000, 2001, 2002, 2003
+   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
@@ -263,7 +264,7 @@ static const CHAR_T zeroes[16] = /* "0000000000000000" */
       int _n = (n);							      \
       int _delta = width - _n;						      \
       int _incr = _n + (_delta > 0 ? _delta : 0);			      \
-      if (i + _incr >= maxsize)						      \
+      if ((size_t) _incr >= maxsize - i)				      \
 	return 0;							      \
       if (p)								      \
 	{								      \
@@ -743,8 +744,15 @@ my_strftime (s, maxsize, format, tp ut_argument LOCALE_PARAM)
 	  width = 0;
 	  do
 	    {
-	      width *= 10;
-	      width += *f - L_('0');
+	      if (width > INT_MAX / 10
+		  || (width == INT_MAX / 10 && *f - L_('0') > INT_MAX % 10))
+		/* Avoid overflow.  */
+		width = INT_MAX;
+	      else
+		{
+		  width *= 10;
+		  width += *f - L_('0');
+		}
 	      ++f;
 	    }
 	  while (ISDIGIT (*f));
@@ -768,10 +776,10 @@ my_strftime (s, maxsize, format, tp ut_argument LOCALE_PARAM)
       switch (format_char)
 	{
 #define DO_NUMBER(d, v) \
-	  digits = width == -1 ? d : width;				      \
+	  digits = d > width ? d : width;				      \
 	  number_value = v; goto do_number
 #define DO_NUMBER_SPACEPAD(d, v) \
-	  digits = width == -1 ? d : width;				      \
+	  digits = d > width ? d : width;				      \
 	  number_value = v; goto do_number_spacepad
 
 	case L_('%'):
@@ -1033,18 +1041,37 @@ my_strftime (s, maxsize, format, tp ut_argument LOCALE_PARAM)
 	      int padding = digits - (buf + (sizeof (buf) / sizeof (buf[0]))
 				      - bufp);
 
-	      if (pad == L_('_'))
-		{
-		  while (0 < padding--)
-		    *--bufp = L_(' ');
-		}
-	      else
+	      if (padding > 0)
 		{
-		  bufp += negative_number;
-		  while (0 < padding--)
-		    *--bufp = L_('0');
-		  if (negative_number)
-		    *--bufp = L_('-');
+		  if (pad == L_('_'))
+		    {
+		      if ((size_t) padding >= maxsize - i)
+			return 0;
+
+		      if (p)
+			memset_space (p, padding);
+		      i += padding;
+		      width = width > padding ? width - padding : 0;
+		    }
+		  else
+		    {
+		      if ((size_t) digits >= maxsize - i)
+			return 0;
+
+		      if (negative_number)
+			{
+			  ++bufp;
+
+			  if (p)
+			    *p++ = L_('-');
+			  ++i;
+			}
+
+		      if (p)
+			memset_zero (p, padding);
+		      i += padding;
+		      width = 0;
+		    }
 		}
 	    }
 
diff --git a/time/tst-strftime.c b/time/tst-strftime.c
new file mode 100644
index 0000000000..1feb741793
--- /dev/null
+++ b/time/tst-strftime.c
@@ -0,0 +1,74 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+
+static struct
+{
+  const char *fmt;
+  size_t min;
+  size_t max;
+} tests[] =
+  {
+    { "%2000Y", 2000, 4000 },
+    { "%02000Y", 2000, 4000 },
+    { "%_2000Y", 2000, 4000 },
+    { "%-2000Y", 2000, 4000 },
+  };
+#define ntests (sizeof (tests) / sizeof (tests[0]))
+
+
+static int
+do_test (void)
+{
+  size_t cnt;
+  int result = 0;
+
+  time_t tnow = time (NULL);
+  struct tm *now = localtime (&tnow);
+
+  for (cnt = 0; cnt < ntests; ++cnt)
+    {
+      size_t size = 0;
+      int res;
+      char *buf = NULL;
+
+      do
+	{
+	  size += 500;
+	  buf = (char *) realloc (buf, size);
+	  if (buf == NULL)
+	    {
+	      puts ("out of memory");
+	      exit (1);
+	    }
+
+	  res = strftime (buf, size, tests[cnt].fmt, now);
+	  if (res != 0)
+	    break;
+	}
+      while (size < tests[cnt].max);
+
+      if (res == 0)
+	{
+	  printf ("%Zu: %s: res == 0 despite size == %Zu\n",
+		  cnt, tests[cnt].fmt, size);
+	  result = 1;
+	}
+      else if (size < tests[cnt].min)
+	{
+	  printf ("%Zu: %s: size == %Zu was enough\n",
+		  cnt, tests[cnt].fmt, size);
+	  result = 1;
+	}
+      else
+	printf ("%Zu: %s: size == %Zu: OK\n", cnt, tests[cnt].fmt, size);
+
+      free (buf);
+    }
+
+  return result;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"