From owner-freebsd-hackers@freebsd.org Sun Jan 17 21:26:20 2021 Return-Path: Delivered-To: freebsd-hackers@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id 9A2604D8C90 for ; Sun, 17 Jan 2021 21:26:20 +0000 (UTC) (envelope-from "") Received: from mout01.posteo.de (mout01.posteo.de [185.67.36.141]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 4DJnzH70L9z4Y1K for ; Sun, 17 Jan 2021 21:26:19 +0000 (UTC) (envelope-from "") Received: from submission (posteo.de [89.146.220.130]) by mout01.posteo.de (Postfix) with ESMTPS id 2B69116005C for ; Sun, 17 Jan 2021 22:26:17 +0100 (CET) Received: from customer (localhost [127.0.0.1]) by submission (posteo.de) with ESMTPSA id 4DJnz52x6mz6tm8; Sun, 17 Jan 2021 22:26:09 +0100 (CET) From: Walter von Entferndt To: Mark Millard Cc: freebsd-hackers@freebsd.org Subject: Re: Implicit assumptions (was: Re: Some fun with -O2) Date: Sun, 17 Jan 2021 22:25:08 +0100 Message-ID: <22093525.sr7ieKrsik@t450s.local.lan> In-Reply-To: References: <1648904.MsCH1bHPGx@t450s.local.lan> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="nextPart3320458.j5jfTYeP2b" Content-Transfer-Encoding: 7Bit X-Rspamd-Queue-Id: 4DJnzH70L9z4Y1K X-Spamd-Bar: -- Authentication-Results: mx1.freebsd.org; dkim=none; dmarc=fail reason="No valid SPF, No valid DKIM" header.from=posteo.net (policy=none); spf=none (mx1.freebsd.org: domain of mout01.posteo.de has no SPF policy when checking 185.67.36.141) smtp.helo=mout01.posteo.de X-Spamd-Result: default: False [-2.70 / 15.00]; RCVD_VIA_SMTP_AUTH(0.00)[]; CTE_CASE(0.50)[]; ARC_NA(0.00)[]; FROM_HAS_DN(0.00)[]; TO_DN_SOME(0.00)[]; NEURAL_HAM_MEDIUM(-1.00)[-1.000]; NEURAL_HAM_LONG(-1.00)[-1.000]; MIME_GOOD(-0.10)[multipart/mixed,text/plain,text/x-patch]; PREVIOUSLY_DELIVERED(0.00)[freebsd-hackers@freebsd.org]; HAS_ATTACHMENT(0.00)[]; RCVD_COUNT_THREE(0.00)[3]; TO_MATCH_ENVRCPT_SOME(0.00)[]; RCVD_IN_DNSWL_MED(-0.20)[185.67.36.141:from]; RCPT_COUNT_TWO(0.00)[2]; NEURAL_HAM_SHORT(-1.00)[-1.000]; R_SPF_NA(0.00)[no SPF record]; FREEMAIL_TO(0.00)[yahoo.com]; R_DKIM_NA(0.00)[]; MIME_TRACE(0.00)[0:+,1:+,2:+]; ASN(0.00)[asn:8495, ipnet:185.67.36.0/23, country:DE]; RCVD_TLS_ALL(0.00)[]; MAILMAN_DEST(0.00)[freebsd-hackers]; DMARC_POLICY_SOFTFAIL(0.10)[posteo.net : No valid SPF, No valid DKIM,none] X-BeenThere: freebsd-hackers@freebsd.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Technical Discussions relating to FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 17 Jan 2021 21:26:20 -0000 This is a multi-part message in MIME format. --nextPart3320458.j5jfTYeP2b Content-Transfer-Encoding: 7Bit Content-Type: text/plain; charset="us-ascii" 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 # include # include +# include /* putenv(), exit(), EXIT_xxx */ +# include /* intmax_t */ +# include /* printf() */ +# include /* CHAR_BIT, INT_MAX */ +# include /* 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) --nextPart3320458.j5jfTYeP2b Content-Disposition: attachment; filename="check_mktime.c.patch" Content-Transfer-Encoding: 7Bit Content-Type: text/x-patch; charset="UTF-8"; name="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 # include # include +# include /* putenv(), exit(), EXIT_xxx */ +# include /* intmax_t */ +# include /* printf() */ +# include /* CHAR_BIT, INT_MAX */ +# include /* 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: */ --nextPart3320458.j5jfTYeP2b--