[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH] sha512-buffer: port back to 32-bit-only hosts
From: |
Bruno Haible |
Subject: |
Re: [PATCH] sha512-buffer: port back to 32-bit-only hosts |
Date: |
Mon, 20 May 2024 13:14:27 +0200 |
Paul Eggert wrote:
> > uintN_t arithmetic is overflow-free, just like 'unsigned int' arithmetic.
>
> Not exactly. For example, this:
>
> uint16_t a = 46341, b = a * a;
>
> has undefined behavior on typical platforms with 32-bit int, because
> 46341*46341 exceeds 2**31 - 1. Although many programmers would consider
> this an example of uint16_t arithmetic, something else is going on.
>
> If uintN_t fits in int, a program cannot do uintN_t arithmetic directly,
> as values are promoted to int before they become arithmetic operands. A
> program can do only int arithmetic on these values, and the int
> arithmetic can overflow.
> ...
> Unsigned int, unsigned long int, unsigned long long int, and uintmax_t
> are the only types where fully portable code can assume wraparound overflow.
Oh, indeed, you're right. And while the gcc and clang UBSAN catches this
(compile with
-fsanitize=undefined,signed-integer-overflow,shift,integer-divide-by-zero),
'gcc -ftrapv' doesn't. [1]
Fortunately we don't use multiplication on uint16_t frequently. (libunistring
uses uint16_t a lot, but never multiplies two uint16_t.)
Also good to know: A gnulib testdir of all modules, compiled with UBSAN,
passes all tests.
> > instead
> > of using the 'u64' module in order to avoid uint64_t, it would be much
> > simpler to make <stdint.h> define the missing int64_t and uint64_t types
>
> We could do something along those lines. I'd prefer it, though, if we
> did that only on platforms where we tested that 'unsigned long long int'
> is actually 64 bits, since the relevant code is already cautious in this
> area due to concerns such as the above. Although platforms are currently
> leery of integers wider than 64 bits, wider integers are commonly
> supported under the covers now, and I expect it won't be forever before
> they emerge from the shadows.
Good point. Done as below. It will make our code more future-proof.
[1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54848#c2
2024-05-20 Bruno Haible <bruno@clisp.org>
stdint: Verify the width of 'long long' before using it as int64_t.
Suggested by Paul Eggert in
<https://lists.gnu.org/archive/html/bug-gnulib/2024-05/msg00315.html>.
* lib/stdint.in.h (gl_int64_t): Verify that the number of bits of
'long long' is 64 before using it.
(gl_uint64_t): Verify that the number of bits of 'unsigned long long'
is 64 before using it.
diff --git a/lib/stdint.in.h b/lib/stdint.in.h
index fea7483b9c..cd3fbdd965 100644
--- a/lib/stdint.in.h
+++ b/lib/stdint.in.h
@@ -80,7 +80,7 @@
#define _@GUARD_PREFIX@_STDINT_H
/* Get SCHAR_MIN, SCHAR_MAX, UCHAR_MAX, INT_MIN, INT_MAX,
- LONG_MIN, LONG_MAX, ULONG_MAX, _GL_INTEGER_WIDTH. */
+ LONG_MIN, LONG_MAX, ULONG_MAX, CHAR_BIT, _GL_INTEGER_WIDTH. */
#include <limits.h>
/* Override WINT_MIN and WINT_MAX if gnulib's <wchar.h> or <wctype.h> overrides
@@ -189,6 +189,10 @@ typedef __int64 gl_int64_t;
# define int64_t gl_int64_t
# define GL_INT64_T
# else
+/* Verify that 'long long' has exactly 64 bits. */
+typedef _gl_verify_int64_bits[
+ _STDINT_MAX (1, sizeof (long long) * CHAR_BIT, 0ll) >> 31 >> 31 == 1
+ ? 1 : -1];
# undef int64_t
typedef long long int gl_int64_t;
# define int64_t gl_int64_t
@@ -210,6 +214,11 @@ typedef unsigned __int64 gl_uint64_t;
# define uint64_t gl_uint64_t
# define GL_UINT64_T
# else
+/* Verify that 'unsigned long long' has exactly 64 bits. */
+typedef _gl_verify_uint64_bits[
+ _STDINT_MAX (0, sizeof (unsigned long long) * CHAR_BIT, 0ull)
+ >> 31 >> 31 >> 1 == 1
+ ? 1 : -1];
# undef uint64_t
typedef unsigned long long int gl_uint64_t;
# define uint64_t gl_uint64_t