From: Georg Sauthoff mail@gms.tf
This fixes -Wunterminated-string-initialization warnings with gcc 15.2.1. --- aclocal.m4 | 7 +++++++ base16-encode.c | 2 +- base64-encode.c | 2 +- base64url-encode.c | 2 +- blowfish-bcrypt.c | 2 +- tools/pkcs1-conv.c | 6 +++--- 6 files changed, 14 insertions(+), 7 deletions(-)
diff --git a/aclocal.m4 b/aclocal.m4 index 73bf0cfb..9f943b45 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -73,6 +73,13 @@ AH_BOTTOM( # define PRINTF_STYLE(f, a) # define UNUSED #endif +#ifdef __has_attribute +# if __has_attribute(nonstring) +# define NONSTRING __attribute__((__nonstring__)) +# else +# define NONSTRING +# endif +#endif ])])
# Check for alloca, and include the standard blurb in config.h diff --git a/base16-encode.c b/base16-encode.c index 9c7f0b1e..c6a4c6b9 100644 --- a/base16-encode.c +++ b/base16-encode.c @@ -39,7 +39,7 @@
static const uint8_t -hex_digits[16] = "0123456789abcdef"; +hex_digits[16] NONSTRING = "0123456789abcdef";
#define DIGIT(x) (hex_digits[(x) & 0xf])
diff --git a/base64-encode.c b/base64-encode.c index ee1ec149..6bb55782 100644 --- a/base64-encode.c +++ b/base64-encode.c @@ -83,7 +83,7 @@ encode_raw(const char *alphabet, assert(out == dst); }
-static const char base64_encode_table[64] = +static const char base64_encode_table[64] NONSTRING = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; diff --git a/base64url-encode.c b/base64url-encode.c index d30044ea..2b78a800 100644 --- a/base64url-encode.c +++ b/base64url-encode.c @@ -38,7 +38,7 @@ void base64url_encode_init(struct base64_encode_ctx *ctx) { - static const char base64url_encode_table[64] = + static const char base64url_encode_table[64] NONSTRING = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789-_"; diff --git a/blowfish-bcrypt.c b/blowfish-bcrypt.c index 385503ac..0326f1ff 100644 --- a/blowfish-bcrypt.c +++ b/blowfish-bcrypt.c @@ -70,7 +70,7 @@ static const signed char radix64_decode_table[0x100] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, };
-static const char radix64_encode_table[64] = +static const char radix64_encode_table[64] NONSTRING = "./ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789"; diff --git a/tools/pkcs1-conv.c b/tools/pkcs1-conv.c index f6b044d2..68bb67d1 100644 --- a/tools/pkcs1-conv.c +++ b/tools/pkcs1-conv.c @@ -117,13 +117,13 @@ read_file(struct nettle_buffer *buffer, FILE *f) }
static const uint8_t -pem_start_pattern[11] = "-----BEGIN "; +pem_start_pattern[11] NONSTRING = "-----BEGIN ";
static const uint8_t -pem_end_pattern[9] = "-----END "; +pem_end_pattern[9] NONSTRING = "-----END ";
static const uint8_t -pem_trailer_pattern[5] = "-----"; +pem_trailer_pattern[5] NONSTRING = "-----";
static const char pem_ws[33] = {
nettle@gms.tf writes:
This fixes -Wunterminated-string-initialization warnings with gcc 15.2.1.
Hmm, I'm to sure its worth the effort to add annotations to this. I see two alternatives:
1. Simply add the trailing NUL byte, e.g., instead of
static const uint8_t -hex_digits[16] = "0123456789abcdef"; +hex_digits[16] NONSTRING = "0123456789abcdef";
change it to
hex_digits[17] = "0123456789abcdef";
or just
hex_digits = "0123456789abcdef";
(will need code adjustments if sizeof is applied to affected string constants).
2. Disable this warning.
Opinions?
Regards, /Niels
Niels Möller nisse@lysator.liu.se writes:
hex_digits[17] = "0123456789abcdef";
That looks ugly but +1, IMHO.
IIRC, I've seen it used like this too:
hex_digits[17] = "0123456789abcdef\0";
A less idiomatic but more tidy approach would be
hex_digits[16] = { '0', '1', '2', ... 'e', 'f' };
I'm hoping no compiler complains about missing ASCII NUL in a "string" defined that way.
Several base64 implementations use the last approach, but mostly for EBCDIC compatibility rather than to pacify false positive compiler warnings. Do Nettle care about EBCDIC targets? Gnulib's base64 code has the snippet below, but it assumes 'char' is 8-bit.
/Simon
/* With this approach this file works independent of the charset used (think EBCDIC). However, it does assume that the characters in the Base64 alphabet (A-Za-z0-9+/) are encoded in 0..255. POSIX 1003.1-2001 require that char and unsigned char are 8-bit quantities, though, taking care of that problem. But this may be a potential problem on non-POSIX C99 platforms.
IBM C V6 for AIX mishandles "#define B64(x) ...'x'...", so use "_" as the formal parameter rather than "x". */ #define B64(_) \ ((_) == 'A' ? 0 \ : (_) == 'B' ? 1 \ : (_) == 'C' ? 2 \ : (_) == 'D' ? 3 \ ... : (_) == '8' ? 60 \ : (_) == '9' ? 61 \ : (_) == '+' ? 62 \ : (_) == '/' ? 63 \ : -1)
signed char const base64_to_int[256] = { B64 (0), B64 (1), B64 (2), B64 (3), ... B64 (252), B64 (253), B64 (254), B64 (255) };
Hello,
On Mon, Apr 13, 2026 at 09:24:17AM +0200, Simon Josefsson wrote: [..]
hex_digits[16] = { '0', '1', '2', ... 'e', 'f' };
I'm hoping no compiler complains about missing ASCII NUL in a "string" defined that way.
The compilers available on gotbolt don't complain:
https://godbolt.org/z/Whb9Eh36q
Several base64 implementations use the last approach, but mostly for EBCDIC compatibility rather than to pacify false positive compiler warnings. Do Nettle care about EBCDIC targets? Gnulib's base64 code
FWIW, I would only worry about EBCDIC after somebody shows up who has a plausible use-case.
has the snippet below, but it assumes 'char' is 8-bit.
I'm sorry, but this gnulib code is horrible.
Why are they over-using the c pre-processor like this ... I mean since gnulib is configure-time checking the world anyway they could simply detect EBCDIC at configure time and conditonally guard/include full ASCII/EBCDIC arrays based on that result. Or even generating the array in M4 or some ultra-portable form of shell at configure time would be a more sensible approach than this.
#define B64(_) \ ((_) == 'A' ? 0 \ : (_) == 'B' ? 1 \ : (_) == 'C' ? 2 \ : (_) == 'D' ? 3 \ ... : (_) == '8' ? 60 \ : (_) == '9' ? 61 \ : (_) == '+' ? 62 \ : (_) == '/' ? 63 \ : -1)
signed char const base64_to_int[256] = { B64 (0), B64 (1), B64 (2), B64 (3), ... B64 (252), B64 (253), B64 (254), B64 (255) };
Best regards, Georg
Georg Sauthoff nettle@gms.tf writes:
Hello,
On Mon, Apr 13, 2026 at 09:24:17AM +0200, Simon Josefsson wrote: [..]
hex_digits[16] = { '0', '1', '2', ... 'e', 'f' };
I'm hoping no compiler complains about missing ASCII NUL in a "string" defined that way.
The compilers available on gotbolt don't complain:
Great! I think that is a reasonable idiom, then.
Several base64 implementations use the last approach, but mostly for EBCDIC compatibility rather than to pacify false positive compiler warnings. Do Nettle care about EBCDIC targets? Gnulib's base64 code
FWIW, I would only worry about EBCDIC after somebody shows up who has a plausible use-case.
+1
IIRC that is what happened for gnulib base64 code, though, but it was a couple of years ago this change was made. Gnulib often goes to effort to be written in a portable way, because its applicability is beyond GNU/Linux. Maybe this isn't applicable to Nettle, but Nettle is a fairly low-level library so I wouldn't be surprised if it is running in EBCDIC environments.
has the snippet below, but it assumes 'char' is 8-bit.
I'm sorry, but this gnulib code is horrible.
Why are they over-using the c pre-processor like this ... I mean since gnulib is configure-time checking the world anyway they could simply detect EBCDIC at configure time and conditonally guard/include full ASCII/EBCDIC arrays based on that result.
That would be unreliable for cross-compilation.
Or even generating the array in M4 or some ultra-portable form of shell at configure time would be a more sensible approach than this.
I'd say the current C code is relatively straight-forward, solves the problem, and compilers end up optimizing it properly anyway. Introducing a pre-processing step seems complicated to me.
I'm not disagreeing that the code is horrible, but I'm not aware of any reliable idiom that solves the requirements in a better way.
/Simon
#define B64(_) \ ((_) == 'A' ? 0 \ : (_) == 'B' ? 1 \ : (_) == 'C' ? 2 \ : (_) == 'D' ? 3 \ ... : (_) == '8' ? 60 \ : (_) == '9' ? 61 \ : (_) == '+' ? 62 \ : (_) == '/' ? 63 \ : -1)
signed char const base64_to_int[256] = { B64 (0), B64 (1), B64 (2), B64 (3), ... B64 (252), B64 (253), B64 (254), B64 (255) };
Best regards, Georg
Hello,
On Sun, Apr 12, 2026 at 10:54:20PM +0200, Niels Möller wrote:
nettle@gms.tf writes:
This fixes -Wunterminated-string-initialization warnings with gcc 15.2.1.
Hmm, I'm to sure its worth the effort to add annotations to this. I see two alternatives:
it didn't feel like much effort to me.
- Simply add the trailing NUL byte, e.g., instead of
static const uint8_t -hex_digits[16] = "0123456789abcdef"; +hex_digits[16] NONSTRING = "0123456789abcdef";
change it to
hex_digits[17] = "0123456789abcdef";
or just
hex_digits = "0123456789abcdef";
(will need code adjustments if sizeof is applied to affected string constants).
I can see how the sizeof change could confuse and trick people, i.e. sounds like a foot-gun one would want to avoid.
Also, even wasting one byte per such array just to silence warnings feels very wrong to me. Slippery slope, embedded systems and all that.
- Disable this warning.
Also similar effort necessary without the benefits of the annotation. The annotation clearly communicates intent, what's going on, and can even be taken into account by static analyzers.
Opinions?
My order of preference is:
a) annotation b) array syntax (cf. Simon's reply)
The disadvantage of the array syntax ( `{ '0', '1', ... }` ) is that it somewhat increases compiler parsing time, but which is hardly measureable. It also increases he source file size slightly.
Best regards, Georg
Georg Sauthoff nettle@gms.tf writes:
- Simply add the trailing NUL byte, e.g., instead of
static const uint8_t -hex_digits[16] = "0123456789abcdef"; +hex_digits[16] NONSTRING = "0123456789abcdef";
change it to
hex_digits[17] = "0123456789abcdef";
or just
hex_digits = "0123456789abcdef";
(will need code adjustments if sizeof is applied to affected string constants).
I can see how the sizeof change could confuse and trick people, i.e. sounds like a foot-gun one would want to avoid.
Indeed -- and I think using sizeof on C string arrays declared with hard-coded magical values can be hard to read. One way around that would be:
#define HEX_ALPHABET_LEN 16 const uint8_t hex_digits[HEX_ALPHABET_LEN] = { '0', ... 'f' };
and to replace all uses of 'sizeof hex_digits' with HEX_ALPHABET_LEN.
/Simon
nettle-bugs@lists.lysator.liu.se