Implicit assumptions (was: Re: Some fun with -O2)
Walter von Entferndt
walter.von.entferndt at posteo.net
Sun Jan 17 21:26:20 UTC 2021
My previous naiive attempt silently assumed CHAR_BIT = 8... Isn't that funny?
Now this is hopefully my last try to fix the issue, it should be agnostic to
any padding and machine word length. It does not cover *time_t* beeing a
floating point type, though. Compile witch 'cc -DDEBUG' to get the
diagnostics to *stderr* .
Regards
--- check_mktime.c.patch ---
--- check_mktime.c.orig 2021-01-15 03:19:33.962253000 +0100
+++ check_mktime.c 2021-01-17 22:01:38.157124000 +0100
@@ -3,6 +3,11 @@
# include <sys/time.h>
# include <time.h>
# include <unistd.h>
+# include <stdlib.h> /* putenv(), exit(), EXIT_xxx */
+# include <stdint.h> /* intmax_t */
+# include <stdio.h> /* printf() */
+# include <limits.h> /* CHAR_BIT, INT_MAX */
+# include <inttypes.h> /* format spec PRIX64: ll/l + X on 32/64-bit arch */
/* Work around redefinition to rpl_putenv by other config tests. */
#undef putenv
@@ -16,6 +21,74 @@
};
#define N_STRINGS (sizeof (tz_strings) / sizeof (tz_strings[0]))
+/* The following two routines (should) work for integer representation
+ in either 2's or 1's complement and for any #bits per byte. */
+
+/* Count the bits set in any integer type.
+ Returns the precision (width - padding bits - sign bit) iff given
+ the xxx_MAX value of any integer type, signed or unsigned.
+ From SEI CERT C Coding Standard:
+ Rules for Developing Safe, Reliable, and Secure Systems (2016) */
+size_t
+popcount (num)
+uintmax_t num;
+{
+ size_t cnt = 0;
+
+ while (num != 0)
+ {
+ if (num % 2 == 1)
+ cnt++;
+ num >>= 1;
+ }
+ return cnt;
+}
+#define PRECISION(max_value) popcount(max_value)
+#define SIGN_BIT (1)
+#ifndef MIN
+#define MIN(a, b) ((a) < (b)? (a): (b))
+#endif
+/* Guess the maximum value of a time_t from it's storage width.
+ On error: returns (time_t)(-1) iff time_t is longer than an intmax_t.
+ ASSERTION: time_t is a signed integer type,
+ i.e. not (unsigned, but the bit pattern of (-1) treated special). */
+time_t
+guess_time_t_max ()
+{
+ time_t t0, t1 = (time_t)(-1);
+ size_t size = sizeof(time_t);
+ size_t prec;
+ uintmax_t max;
+ uint64_t u64;
+
+ if (sizeof(time_t) > sizeof(uintmax_t))
+ return t1;
+
+ /* Get an uintmax_t with all bits set that could be in a time_t.
+ We can not do this calculation with a time_t as long we do not
+ know its precision (overflow could occur). */
+ prec = MIN (PRECISION (UINTMAX_MAX), CHAR_BIT * sizeof(time_t));
+ max = (uintmax_t) 1 << (prec - 1);
+ max = max|(max - 1);
+
+ t0 = max; /* maybe trucation happens here */
+
+ /* Now account for any padding */
+ prec = PRECISION(t0) - SIGN_BIT;
+ t0 = (time_t) 1 << (prec - 1);
+ t1 = t0|(t0 - 1);
+
+#ifdef DEBUG
+ fprintf (stderr, "time_t_max\t= 0x%"PRIX64"\n", (u64 = t1));
+ fprintf (stderr, "sizeof(time_t)\t= %2zd byte\n", size);
+ fprintf (stderr, "precision\t= %2zd bit\n", prec);
+ fprintf (stderr, "padding\t\t= %2zd bit\n", CHAR_BIT*size - prec -
SIGN_BIT);
+ fprintf (stderr, "bits per byte\t= %2d bit\n", CHAR_BIT);
+#endif /* DEBUG */
+ return t1;
+}
+#undef SIGN_BIT
+
/* Fail if mktime fails to convert a date in the spring-forward gap.
Based on a problem report from Andreas Jaeger. */
static void
@@ -37,8 +110,8 @@
tm.tm_min = 0;
tm.tm_sec = 0;
tm.tm_isdst = -1;
- if (mktime (&tm) == (time_t)-1)
- exit (1);
+ if (mktime (&tm) == (time_t)(-1))
+ exit (EXIT_FAILURE);
}
static void
@@ -47,10 +120,10 @@
{
struct tm *lt;
if ((lt = localtime (&now)) && mktime (lt) != now)
- exit (1);
+ exit (EXIT_FAILURE);
now = time_t_max - now;
if ((lt = localtime (&now)) && mktime (lt) != now)
- exit (1);
+ exit (EXIT_FAILURE);
}
static void
@@ -67,7 +140,7 @@
tm.tm_isdst = -1;
mktime (&tm);
if (tm.tm_mon != 2 || tm.tm_mday != 31)
- exit (1);
+ exit (EXIT_FAILURE);
}
static void
@@ -82,7 +155,7 @@
alarm (10);
now = mktime (&tm);
alarm (0);
- if (now != (time_t) -1)
+ if (now != (time_t)(-1))
{
struct tm *lt = localtime (&now);
if (! (lt
@@ -96,7 +169,7 @@
&& lt->tm_wday == tm.tm_wday
&& ((lt->tm_isdst < 0 ? -1 : 0 < lt->tm_isdst)
== (tm.tm_isdst < 0 ? -1 : 0 < tm.tm_isdst))))
- exit (1);
+ exit (EXIT_FAILURE);
}
}
@@ -106,9 +179,10 @@
time_t t, delta;
int i, j;
- for (time_t_max = 1; 0 < time_t_max; time_t_max *= 2)
- continue;
- time_t_max--;
+ time_t_max = guess_time_t_max ();
+ if (time_t_max == (time_t)(-1))
+ exit (EXIT_FAILURE);
+
delta = time_t_max / 997; /* a suitable prime number */
for (i = 0; i < N_STRINGS; i++)
{
@@ -120,11 +194,15 @@
mktime_test ((time_t) 60 * 60);
mktime_test ((time_t) 60 * 60 * 24);
- for (j = 1; 0 < j; j *= 2)
+ /* NOTE This does not reach very large values > time_t_max/2
+ neither the (susceptible for overflow) version
+ for (j = 1; 0 < j; j *= 2) does */
+ for (i = j = 1; i < PRECISION(INT_MAX); i++, j *= 2)
bigtime_test (j);
bigtime_test (j - 1);
}
irix_6_4_bug ();
spring_forward_gap ();
- exit (0);
+ exit (EXIT_SUCCESS);
}
+/*! vi: set ai tabstop=8 shiftwidth=2: */
--
=|o) "Stell' Dir vor es geht und keiner kriegt's hin." (Wolfgang Neuss)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: check_mktime.c.patch
Type: text/x-patch
Size: 4824 bytes
Desc: not available
URL: <http://lists.freebsd.org/pipermail/freebsd-hackers/attachments/20210117/fc27d2af/attachment.bin>
More information about the freebsd-hackers
mailing list