Skip to content

Fixed -Wunterminated-string-initialization with GCC 15. #134

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from

Conversation

trofi
Copy link

@trofi trofi commented Sep 7, 2024

gcc-15 added a new warning in https://gcc.gnu.org/PR115185:

src/http/v2/ngx_http_v2_filter_module.c: In function
  'ngx_http_v2_header_filter':
src/http/v2/ngx_http_v2_filter_module.c:118:36: error:
  initializer-string for array of 'unsigned char' is too long
    [-Werror=unterminated-string-initialization]
  118 |     static const u_char nginx[5] = "\x84\xaa\x63\x55\xe7";
      |                                    ^~~~~~~~~~~~~~~~~~~~~~
src/http/v2/ngx_http_v2_filter_module.c:121:9: error:
  initializer-string for array of 'unsigned char' is too long
    [-Werror=unterminated-string-initialization]
  121 |         "\x8b\x84\x84\x2d\x69\x5b\x05\x44\x3c\x86\xaa\x6f";
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This change initializes the array bytewise to avoid string literal
assignnment (and avoid trailing "\0" truncation).

@arut
Copy link
Contributor

arut commented Sep 9, 2024

Seems like gcc-15 is in its early stages, so maybe it's too early to fix this. Generally I like the patch, just a couple of small comments. Plus the commit log should be in line with nginx standards, see https://github.com/nginx/nginx/blob/master/CONTRIBUTING.md.

@trofi trofi force-pushed the gcc-15-string-overwrite branch from 51b6e1d to df5486a Compare September 9, 2024 20:28
@trofi trofi changed the title ngx_http_v2_filter_module: fix gcc-15 build failure HTTP/2: fix gcc-15 build failure on -Werror=unterminated-string-initialization. Sep 9, 2024
@trofi
Copy link
Author

trofi commented Sep 9, 2024

Reworded the commit message.

@arut arut self-assigned this Sep 10, 2024
Copy link
Contributor

@pluknet pluknet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The commit misses the same issues in the QUIC code.

@trofi trofi force-pushed the gcc-15-string-overwrite branch from df5486a to 08f6439 Compare September 26, 2024 21:49
@trofi trofi changed the title HTTP/2: fix gcc-15 build failure on -Werror=unterminated-string-initialization. HTTP/2, QUIC: fix gcc-15 build failure on -Werror=unterminated-string-initialization. Sep 26, 2024
@trofi
Copy link
Author

trofi commented Sep 26, 2024

Good point. Applied the similar change to src/event/quic/ngx_event_quic_protection.c as well.

Copy link
Contributor

@pluknet pluknet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concerning the commit log.

We don't use multiple prefixes in the commit log.
If commit comes as a general fix touching unrelated places, the prefix is omitted.

Looking at the commit history to get similar commit messages, this leads to the next simple commit log:

Fixed -Wunterminated-string-initialization with GCC 15.

Comment on lines 125 to 128
static const uint8_t salt[20] = {
0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17,
0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a,
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is a change in the right direction.

What I'd like to see is declaring with unspecified array size, e.g. similar to the grpc module. This allows to keep using the initialization string unmodified, written in a more compact form, compared to a byte sequence.

Calculating the number of array elements with sizeof() - 1 is a common approach used in nginx as well.

$ grep -E 'sizeof\([^)]+\) - 1' src/http/v2/ngx_http_v2_filter_module.c
    (ngx_http_v2_integer_octets(sizeof(h) - 1) + sizeof(h) - 1)
            len += 1 + sizeof(nginx) - 1;
            len += sizeof("; charset=") - 1 + r->headers_out.charset.len;
            location.len = sizeof("https://") - 1 + host.len
                location.len += sizeof(":65535") - 1;
            p = ngx_cpymem(location.data, "http", sizeof("http") - 1);
            len += 1 + sizeof(accept_encoding) - 1;
                                            sizeof(NGINX_VER) - 1, tmp);
                                            sizeof(NGINX_VER_BUILD) - 1, tmp);
            pos = ngx_cpymem(pos, nginx, sizeof(nginx) - 1);
            len = r->headers_out.content_type.len + sizeof("; charset=") - 1
            p = ngx_cpymem(p, "; charset=", sizeof("; charset=") - 1);
        len = sizeof("Wed, 31 Dec 1986 18:00:00 GMT") - 1;
        pos = ngx_cpymem(pos, accept_encoding, sizeof(accept_encoding) - 1);

This covers both C arrays and literal strings.

As a bonus, this allows logging the salt value without variable length modifier:

@@ -152,8 +152,8 @@ ngx_quic_keys_set_initial_secret(ngx_quic_keys_t *keys, ngx_str_t *secret,
     ngx_log_debug0(NGX_LOG_DEBUG_EVENT, log, 0,
                    "quic ngx_quic_set_initial_secret");
 #ifdef NGX_QUIC_DEBUG_CRYPTO
-    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, log, 0,
-                   "quic salt len:%uz %*xs", sizeof(salt), sizeof(salt), salt);
+    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0,
+                   "quic salt len:%uz %xs", sizeof(salt) - 1, salt);
     ngx_log_debug3(NGX_LOG_DEBUG_EVENT, log, 0,
                    "quic initial secret len:%uz %*xs", is_len, is_len, is);
 #endif

`gcc-15` added a new warning in https://gcc.gnu.org/PR115185:

    src/http/v2/ngx_http_v2_filter_module.c: In function
      'ngx_http_v2_header_filter':
    src/http/v2/ngx_http_v2_filter_module.c:118:36: error:
      initializer-string for array of 'unsigned char' is too long
        [-Werror=unterminated-string-initialization]
      118 |     static const u_char nginx[5] = "\x84\xaa\x63\x55\xe7";
          |                                    ^~~~~~~~~~~~~~~~~~~~~~
    src/http/v2/ngx_http_v2_filter_module.c:121:9: error:
      initializer-string for array of 'unsigned char' is too long
        [-Werror=unterminated-string-initialization]
      121 |         "\x8b\x84\x84\x2d\x69\x5b\x05\x44\x3c\x86\xaa\x6f";
          |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This change initializes the array bytewise to avoid string literal
assignnment (and avoid trailing "\0" truncation).
@trofi trofi force-pushed the gcc-15-string-overwrite branch from 08f6439 to df14b77 Compare September 27, 2024 21:44
@trofi trofi changed the title HTTP/2, QUIC: fix gcc-15 build failure on -Werror=unterminated-string-initialization. Fixed -Wunterminated-string-initialization with GCC 15. Sep 27, 2024
@trofi
Copy link
Author

trofi commented Sep 27, 2024

Switched salt to implicit array length, changed commit subject.

@pluknet
Copy link
Contributor

pluknet commented Sep 29, 2024

Please apply same in the rest places.
In the commit log, please remove compiler diagnostics, as it serves no purpose, and remove then to-be out-of-date bytewise statement.

@pluknet
Copy link
Contributor

pluknet commented Sep 30, 2024

On the other hand, removing explicit size from key_data and nonce may be error-prone, because they are passed without explicit size (the object size is implied by a selected cipher suite). And it could be a security issue, e.g. once NGX_QUIC_IV_LEN is enlarged.
On the other hand #2, key size and nonce size don't depend on a cipher, the lengths (and values) and statically defined in the spec.
So, it could be barely an issue if we would add a yet another QUIC version.

@pgnd
Copy link

pgnd commented Feb 7, 2025

@arut

iiuc this was closed & not merged?
was this fixed elsewhere?

i hit it with current release + GCC15, as on Fedora 42+. e.g.:

for Fedora41-release, with GCC14

@ https://gist.github.com/pgnd/23a3dd25ec9fe19d34f1b0850323d734#file-gistfile1-txt-L4900

...
 njs build dir: build
 njs CLI: build/njs

	chmod 755 blib/arch/auto/nginx/nginx.so
	make[2]: Entering directory '/builddir/build/BUILD/nginx-git_release_1.27.4-build/njs-master'
	/usr/bin/gcc -c -Isrc -Iexternal -Ibuild -pipe -fPIC -fvisibility=hidden -O -W -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wmissing-prototypes -Werror -g -fexcess-precision=standard -O -O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -march=x86-64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -mtls-dialect=gnu2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -std=gnu17 -O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -march=x86-64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -mtls-dialect=gnu2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -std=gnu17 -Wno-deprecated-declarations \
		 \
		-o build/src/njs_diyfp.o \
		-MMD -MF build/src/njs_diyfp.dep -MT build/src/njs_diyfp.o \
		src/njs_diyfp.c
	Manifying 1 pod document
	make[2]: Leaving directory '/builddir/build/BUILD/nginx-git_release_1.27.4-build/nginx-nginx-005f01b/objs/src/http/modules/perl'
...

for Fedora42-prerelease, with GCC15

@ https://gist.github.com/pgnd/f40533deaea3f1defe82906bee25c77d#file-gistfile1-txt-L3669

...
 njs build dir: build
 njs CLI: build/njs

	make[2]: Entering directory '/builddir/build/BUILD/nginx-git_release_1.27.4-build/njs-master'
	/usr/bin/gcc -c -Isrc -Iexternal -Ibuild -pipe -fPIC -fvisibility=hidden -O -W -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wmissing-prototypes -Werror -g -fexcess-precision=standard -O -O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -march=x86-64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -mtls-dialect=gnu2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -std=gnu17 -O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -march=x86-64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -mtls-dialect=gnu2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -std=gnu17 -Wno-deprecated-declarations \
		 \
		-o build/src/njs_diyfp.o \
		-MMD -MF build/src/njs_diyfp.dep -MT build/src/njs_diyfp.o \
		src/njs_diyfp.c
	In file included from src/njs_main.h:59,
	                 from src/njs_diyfp.c:12:
	src/njs_string.h: In function ‘njs_string_encode’:
	src/njs_string.h:212:36: error: initializer-string for array of ‘unsigned char’ is too long [-Werror=unterminated-string-initialization]
	  212 |     static const u_char  hex[16] = "0123456789ABCDEF";
	      |                                    ^~~~~~~~~~~~~~~~~~
	cc1: all warnings being treated as errors
	make[2]: *** [build/Makefile:106: build/src/njs_diyfp.o] Error 1
	make[2]: Leaving directory '/builddir/build/BUILD/nginx-git_release_1.27.4-build/njs-master'
	make[1]: *** [objs/Makefile:1725: ../njs-master/nginx/../build/libnjs.a] Error 2
	make[1]: *** Waiting for unfinished jobs....
	make[1]: Leaving directory '/builddir/build/BUILD/nginx-git_release_1.27.4-build/nginx-nginx-005f01b'
	make: *** [Makefile:10: build] Error 2
	error: Bad exit status from /var/tmp/rpm-tmp.4GqmeD (%build)
...

@pluknet
Copy link
Contributor

pluknet commented Feb 7, 2025

Looks like it was closed as a coincidence.
If the issue continues to remain valid with the upcoming GCC 15 release (i.e. GCC developers won't back out this warning; at the time of writing, it is in stage 4, so there's still a change), I plan to get back to it to make nginx compilable.
Until then there is a workaround ./configure --with-cc-opt="-Wno-error=unterminated-string-initialization"

@pgnd
Copy link

pgnd commented Feb 7, 2025

If the issue continues ... plan to get back to it to make nginx compilable.

got it, thx.

workaround

yup. the workaround works for now; GCC15 builds for F42 & Rawhide are good.

@trofi
Copy link
Author

trofi commented Feb 7, 2025

It was closed because the scope of the fix kept growing without the sight of completion. The comments were too vague for me to clearly understand the desired end state.

After another merge conflict I gave up. It's just taking too much time from me to fix a trivial issue. I ended up disabling -Werror locally as well.

@ac000
Copy link
Member

ac000 commented Feb 11, 2025

FWIW we hit the same issue in Unit.

For now we've disabled this warning until hopefully this patch (or similar) makes it into GCC then we can re-enable the warning and tag such arrays with the "nonstring" variable attribute.

@BwL1289
Copy link

BwL1289 commented Mar 16, 2025

FWIW "-Wno-error=unterminated-string-initialization" works for clang as well.

@ac000
Copy link
Member

ac000 commented Mar 16, 2025

FWIW --with-cc-opt="-Wno-error=unterminated-string-initialization" works for clang as well.

Hmm, which version of clang is that? AFAICT clang has no such option...

However seeing as the above mentioned patch has now been merged into GCC 15 this issue can be handled properly...

@pgnd
Copy link

pgnd commented Mar 16, 2025

@ac000

can be handled properly

do you have a link for that?

@ac000
Copy link
Member

ac000 commented Mar 16, 2025

@ac000

can be handled properly

do you have a link for that?

The linked commit will allow __attribute__(("nonstring")) to silence this warning, e.g. you will be able to do

static const char hex[16] __attribute__(("nonstring")) = "0123456789ABCDEF";

This will allow you to stick to defining string literals as str[] and character arrays as str[N], with the difference of course being the str[] will be NUL terminated and the str[N] won't. This keeps the two use cases clearly defined.

E.g.

For something like

int hexit(int c)
{
	static const char hex[16] = "0123456789ABCDEF";

	return hex[c % 16];
}

GCC 15 now produces

$ gcc -Wunterminated-string-initialization -c nonstring.c nonstring.c: In function ‘hexit’:
nonstring.c:13:49: warning: initializer-string for array of ‘char’ truncates NUL terminator but destination lacks ‘nonstring’ attribute (17 chars into 16 available) [-Wunterminated-string-initialization]
   13 |         static const char hex[16] = "0123456789ABCDEF";
      |                                     ^~~~~~~~~~~~~~~~~~

(Note: -Wunterminated-string-initialization is enabled by -Wextra)

So you can do

int hexit(int c)                                                                
{                                                                               
        static const char hex[16] __attribute__((nonstring)) = "0123456789ABCDEF";                                                                              
                                                                                
        return hex[c % 16];                                                     
}
$ gcc -Wunterminated-string-initialization -c nonstring.c 
$

This attribute already existed, it just didn't originally effect this new warning.

ac000 added a commit to ac000/nginx that referenced this pull request Apr 15, 2025
This is a wrapper around __attribute__((nonstring)). Traditionally this
was used to mark char array variables that intentionally lacked a
terminating NUL byte, this would then cause warnings to either be
quelled or emitted for various memory/string functions.

GCC 15 introduced a new warning, Wunterminated-string-initialization,
which will always warn on things like

  static const char hex[16] = "0123456789ABCDEF";

However this is very much intentionally not NUL terminated.

The above attribute also quells this new warning (which is also enabled
by -Wextra).

So the above example would become

  static const char hex[16] NGX_NONSTRING = "0123456789ABCDEF";

This issue was attempted to be fixed in a previous pull-request, but I
believe this to be a superior solution (although the use of "nonstring"
to quell this warning wasn't available at that time).

Link: <https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=44c9403ed1833ae71a59e84f9e37af3182be0df5>
Link: <https://gcc.gnu.org/git/gitweb.cgi?p=gcc.git;h=622968990beee7499e951590258363545b4a3b57>
Link: <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117178#c21>
Link: <nginx#134>
Signed-off-by: Andrew Clayton <a.clayton@nginx.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants