Any suspicious activity such as DATA frames to a stream which does not
exist are counted to so called "glitch" counter. If it increases more
than the configured rate, GOAWAY is sent and the connection is closed.
Stream errors are now promoted to connection errors. This means that
an event that previously just resets a single stream now closes a
connection entirely. The promoted errors are mostly implementation
errors. Some involve HTTP fields, but they are already treated stream
error. People who care about that should have already raised any
issues. We do not have any outstanding related issues now, so it
seems OK to treat it as connection error.
We have some contradictory specifications around
nghttp2_on_invalid_header and nghttp2_on_invalid_header2 callbacks.
nghttp2_on_invalid_header says that if it is omitted, a stream is
reset. Meanwhile, nghttp2_on_invalid_header2 says that if it is
omitted, invalid field is silently ignored. In actual implementation,
if both omitted, we treat it as stream error. In practice, it is
often required not to bail out if invalid header is received. In this
change, if both callbacks are omitted, invalid field is silently
ignored as the documentation of nghttp2_on_invalid_header2 says. The
connection error promotion is applied here as well. So if invalid
field is received, and callback returns
NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE, it is treated as connection
error.
Do not submit RST_STREAM more than once for a same stream with
nghttp2_submit_rst_stream. Historically, nghttp2_submit_rst_stream
allows this. nghttp2 also allows receiving multiple RST_STREAM
frames. To keep compatibility, nghttp2_submit_rst_stream does not
fail if it attempts to submit RST_STREAM to already closed stream.
Summary of the behavioral changes in public API functions:
- nghttp2_session_change_stream_priority: This function is noop. It
always returns 0.
- nghttp2_session_create_idle_stream: This function is noop. It
always returns 0.
- nghttp2_submit_request: pri_spec is ignored.
- nghttp2_submit_request2: pri_spec is ignored.
- nghttp2_submit_headers: pri_spec is ignored.
- nghttp2_submit_priority: This function is noop. It always returns
0.
- nghttp2_stream_get_parent: This function always returns NULL.
- nghttp2_stream_get_next_sibling: This function always returns NULL.
- nghttp2_stream_get_previous_sibling: This function always returns
NULL.
- nghttp2_stream_get_first_child: This function always returns NULL.
- nghttp2_stream_get_weight: This function always returns
NGHTTP2_DEFAULT_WEIGHT.
- nghttp2_stream_get_sum_dependency_weight: This function always
returns 0.
Remove stream from queue on deletion. Run on_stream_close_callback
before deallocating item in case that a frame is submitted in the
callback which is highly unusual in practice.
This commit deprecates all APIs, including structs and callback
functions, that use ssize_t. New APIs that use nghttp2_ssize are
added as a replacement. The usage of ssize_t is problematic for
several reasons. Some platforms do not define ssize_t. The minimum
value of ssize_t that POSIX requires is -1 which makes nghttp2 error
code out of range. nghttp2_ssize is an alias of ptrdiff_t that is in
C standard and covers our error code range.
New code should use new nghttp2_ssize APIs. The existing applications
should consider migrating to new APIs.
The deprecated ssize_t APIs continue to work for backward
compatibility.
Here is the summary of the deprecated APIs and their replacements:
Callback functions:
- nghttp2_data_source_read_callback => nghttp2_data_source_read_callback2
- nghttp2_data_source_read_length_callback => nghttp2_data_source_read_length_callback2
- nghttp2_pack_extension_callback => nghttp2_pack_extension_callback2
- nghttp2_recv_callback => nghttp2_recv_callback2
- nghttp2_select_padding_callback => nghttp2_select_padding_callback2
- nghttp2_send_callback => nghttp2_send_callback2
Structs:
- nghttp2_data_provider => nghttp2_data_provider2
Functions:
- nghttp2_hd_deflate_hd => nghttp2_hd_deflate_hd2
- nghttp2_hd_deflate_hd_vec => nghttp2_hd_deflate_hd_vec2
- nghttp2_hd_inflate_hd2 => nghttp2_hd_inflate_hd3
- nghttp2_pack_settings_payload => nghttp2_pack_settings_payload2
- nghttp2_session_callbacks_set_data_source_read_length_callback =>
nghttp2_session_callbacks_set_data_source_read_length_callback2
- nghttp2_session_callbacks_set_pack_extension_callback =>
nghttp2_session_callbacks_set_pack_extension_callback2
- nghttp2_session_callbacks_set_recv_callback =>
nghttp2_session_callbacks_set_recv_callback2
- nghttp2_session_callbacks_set_select_padding_callback =>
nghttp2_session_callbacks_set_select_padding_callback2
- nghttp2_session_callbacks_set_send_callback =>
nghttp2_session_callbacks_set_send_callback2
- nghttp2_session_mem_recv => nghttp2_session_mem_recv2
- nghttp2_session_mem_send => nghttp2_session_mem_send2
- nghttp2_submit_data => nghttp2_submit_data2
- nghttp2_submit_request => nghttp2_submit_request2
- nghttp2_submit_response => nghttp2_submit_response2
For those applications that do not want to see ssize_t in nghttp2.h
header file, define NGHTTP2_NO_SSIZE_T macro before including
nghttp2.h. It hides all ssize_t APIs.
This commit fixes memory leak that happens when PUSH_PROMISE or
HEADERS frame cannot be sent, and nghttp2_on_stream_close_callback
fails with a fatal error. For example, if GOAWAY frame has been
received, a HEADERS frame that opens new stream cannot be sent.
This issue has already been made public via CVE-2023-35945 [1] issued
by envoyproxy/envoy project. During embargo period, the patch to fix
this bug was accidentally submitted to nghttp2/nghttp2 repository [2].
And they decided to disclose CVE early. I was notified just 1.5 hours
before disclosure. I had no time to respond.
PoC described in [1] is quite simple, but I think it is not enough to
trigger this bug. While it is true that receiving GOAWAY prevents a
client from opening new stream, and nghttp2 enters error handling
branch, in order to cause the memory leak,
nghttp2_session_close_stream function must return a fatal error.
nghttp2 defines 2 fatal error codes:
- NGHTTP2_ERR_NOMEM
- NGHTTP2_ERR_CALLBACK_FAILURE
NGHTTP2_ERR_NOMEM, as its name suggests, indicates out of memory. It
is unlikely that a process gets short of memory with this simple PoC
scenario unless application does something memory heavy processing.
NGHTTP2_ERR_CALLBACK_FAILURE is returned from application defined
callback function (nghttp2_on_stream_close_callback, in this case),
which indicates something fatal happened inside a callback, and a
connection must be closed immediately without any further action. As
nghttp2_on_stream_close_error_callback documentation says, any error
code other than 0 or NGHTTP2_ERR_CALLBACK_FAILURE is treated as fatal
error code. More specifically, it is treated as if
NGHTTP2_ERR_CALLBACK_FAILURE is returned. I guess that envoy returns
NGHTTP2_ERR_CALLBACK_FAILURE or other error code which is translated
into NGHTTP2_ERR_CALLBACK_FAILURE.
[1] https://github.com/envoyproxy/envoy/security/advisories/GHSA-jfxv-29pc-x22r
[2] https://github.com/nghttp2/nghttp2/pull/1929
Add nghttp2_check_header_value_rfc9113 which verifies the additional
rule imposed by RFC 9113, section 8.2.1, that is a field value must
not start or end with 0x20(SPC) or 0x09(HTAB).
libnghttp2 uses this new function internally.
Add nghttp2_option_set_server_fallback_rfc7540_priorities. If it is
set to nonzero, and server submits SETTINGS_NO_RFC7540_PRIORITIES = 1,
but it does not receive SETTINGS_NO_RFC7540_PRIORITIES from client,
server falls back to RFC 7540 priorities. Only minimal set of
features are enabled in this fallback case.
This commit adds PRIORITY_UPDATE frame support. Applying incoming
PRIORITY_UPDATE frame to server push stream is not implemented.
Client can send PRIORITY_UPDATE frame by calling
nghttp2_submit_priority_update.
Server opts to receive PRIORITY_UPDATE frame by the call
nghttp2_option_set_builtin_recv_extension_type(option,
NGHTTP2_PRIORITY_UPDATE), and passing the option to
nghttp2_session_server_new2 or nghttp2_session_server_new3.
This commit implements RFC 9218 extensible prioritization scheme. It
is enabled when a local endpoint submits
SETTINGS_NO_RFC7540_PRIORITIES = 1. This commit only handles priority
signal in HTTP request header field. Priority header field in
PUSH_PROMISE is not supported.
HTTP messaging must be enabled to take advantage of this
prioritization scheme because HTTP fields are not parsed if HTTP
messaging is disabled.
Fix the bug that causes a stream to stall when a receiver, which
enables nghttp2_option_set_no_auto_window_update() option on, sends
SETTINGS_INITIAL_WINDOW_SIZE with the value that is less than or equal
to the amount of data received. Previously, in this particular case,
when SETTINGS is acknowledged by the sender, the receiver does not try
to send WINDOW_UPDATE frame. The sender is unable to send more data
because its stream-level window size is smaller than or equal to the
amount of data it has sent.