Complex value compilation scans strings for $1..$9 capture references.
Check that a byte after '$' is present before testing it, matching
ngx_str_t length semantics and avoiding reliance on NUL termination.
Apply the same check to both HTTP and stream implementations.
When processing CONTINUATION frames, ngx_http_v2_handle_continuation()
used ngx_memcpy() to shift header block fragment data past the frame
header. If the fragment is larger than the frame header (9 bytes),
the source and destination regions overlap, which is undefined
behavior for memcpy. The same function already uses ngx_memmove()
for another overlapping shift.
With prerequisites similar to 696a7f1b9, it was possible to gain 1-byte
overread on invalid UTF-8 sequences. The reason is ngx_utf8_decode()
stops advancing the pointer position on the first encountered invalid
byte. The fix is to adjust the advanced pointer up to the whole saved
sequence in this case. Note that this may result in different output
compared to complete invalid UTF-8 sequences, which we can disregard
at this point.
Reported by Han Yan of Xiaomi and p4p3r of CYBERONE.
Creating a control/encoder/decoder stream while another such stream
already exists, is not allowed. Also, closing such a stream results
in connection closure with NGX_HTTP_V3_ERR_CLOSED_CRITICAL_STREAM.
However, since stream creation and connection closure are asynchronous,
there could be a window where two control/encoder/decoder streams
could coexist within a single cycle iteration. This could result
in reusing parsing context, such as encoder insert buffer.
The change adds a mask for all standard client uni streams ever created.
This allows to check if a stream of this type was created before.
While here, mandatory stream validation now also uses this mask.
Previously, it was allocated from the encoder stream pool. This could
lead to use-after-free if the stream was closed and another encoder
stream was opened.
Reported by Trung Nguyen (@everping) of CyStack.
If the last variant was given an explicit percentage to reach 100%, it
did not cover hash values on the range boundary or due to accumulating
rounding error. Now this corresponds to the configuration with "*".
The new $ssl_sigalgs variable lists all signature algorithms
advertised by the client in its ClientHello message,
colon-separated, in analogy to $ssl_ciphers and $ssl_curves.
On OpenSSL 4.0+, SSL_get0_sigalg() is used, which returns proper
TLS scheme names (e.g. "rsa_pkcs1_sha256", "ecdsa_secp256r1_sha256")
and falls back to "0xHHHH" hex for unknown schemes.
On OpenSSL 1.0.2+, SSL_get_sigalgs() is used. Since OBJ_nid2sn()
on the combined psignhash NID is not injective across TLS
SignatureScheme codes (e.g. 0x0403 and 0x081a both yield
"ecdsa-with-SHA256"), raw SignatureScheme codes are reported to
avoid ambiguity and to match the hex fallback format of
SSL_get0_sigalg().
With older versions, as well as BoringSSL, LibreSSL, and AWS-LC,
the variable is empty.
Replaced RAND_bytes() and random() based $request_id
generation with SipHash-2-4. The previous implementation
used RAND_bytes() when built with OpenSSL, which is
50-100x slower than needed for a non-security identifier,
and fell back to random() which produces poor 128-bit
random numbers.
The new implementation calls SipHash twice with a secret
key and a monotonic counter, producing 128 bits with good
statistical distribution at a fraction of the cost.
When the rewrite replacement string had no variables, but had
overlapping captures, the length of the allocated buffer could be
smaller than the replacement string. This could happen either
when the "redirect" parameter is specified, or when arguments are
present in the replacement string.
The following configurations resulted in heap buffer overflow when
using URI "/++++++++++++++++++++++++++++++":
location / {
rewrite ^/((.*))$ http://127.0.0.1:8080/$1$2 redirect;
return 200 foo;
}
location / {
rewrite ^/((.*))$ http://127.0.0.1:8080/?$1$2;
return 200 foo;
}
Reported by Mufeed VH of Winfunc Research.
Following 2046b45aa0, this change introduces better control of memory
allocation flags for escaped values. Notably:
- The e->is_args flag is now explicitly reset on rewrite start.
If the flag was set prior to rewrite start, then buffer overflow
could happen before 2046b45aa0.
- The le->is_args flag value is now copied from e->is_args when
calculating complex value length for "if" and "set" directives.
If e->is_args was set, but le->is_args was not, then buffer overflow
could happen before 2046b45aa0.
Previously, when these fields were larger than ~2M, the number of bytes
allocated for the field length was insufficient for such a large number.
The deficit is 1 byte up until ~4M, 2 bytes for sizes above, and grows
bigger with even larger fields.
Currently, nginx does not have modules which allow to exploit this
overflow with reasonably large Content-Type and Location. The reason is
other response fields make up for this deficit. For example, the Date
header value contains the characters compressed well by Huffman
encoding, which frees up spare bytes in the header buffer.
Reported by Leo Lin.
Previously, if proxy_set_body was used with HTTP/2, and body size exceeded
16M, then an overflow happened in the 24-bit DATA frame size, which resulted
in sending unframed bytes, potentially allowing for an injection.
Also, DATA frame size could exceed NGX_HTTP_V2_DEFAULT_FRAME_SIZE (16K) and
available send window.
Reported by Mufeed VH of Winfunc Research.
The following code resulted in incorrect escaping of $1 and possible
segfault:
location / {
rewrite ^(.*) /new?c=1;
set $myvar $1;
return 200 $myvar;
}
If there were arguments in a rewrite's replacement string, the is_args flag
was set and incorrectly never cleared. This resulted in escaping applied
to any captures evaluated afterwards in set or if. Additionally buffer was
allocated by ngx_http_script_complex_value_code() without escaping expected,
thus this also resulted in buffer overrun and possible segfault.
A similar issue was fixed in 74d939974d.
Reported by Leo Lin.
If the first response line was split across reads and it didn't appear
a status line, the portion already processed was lost. The change
introduces a new field for proper backtracking on status line fallback.
Previously, it was possible to start parsing headers with a wrong
parsing state after status line was not recognized, as a fallback
used in the scgi and uwsgi modules.
Reported by Leo Lin.
When a multi-byte UTF-8 character was split across 3+ single-byte
buffers, the saved bytes continuation path had two related bugs:
ngx_utf8_decode() was called with the last saved-array index instead
of the byte count, causing it to report "incomplete" even when the
sequence was already complete.
The subsequent ngx_memcpy() used that same index as the copy length,
reading past the input buffer boundary.
Notably, "auth_delay" now delays the response for both 401 and 407.
Also, in the "satisfy any" mode, the next access/auth attempt is made
for 401, 403 and 407.
Previously, when an HTTP/2 request had no body or an explicit body
was set by proxy_set_body, the request consisted of only one buffer,
which had no b->last_buf flag set. This prevented ctx->output_closed
from being set after processing this buffer. Consequently,
u->keepalive might not be set to store the connection in the
keepalive cache.
The COPY and MOVE handler did not validate whether source and
destination paths referred to the same resource or a parent-child
collection relationship, which could corrupt or destroy files.
Now 403 is returned if paths match or one is a prefix of the other.
Reported by Mufeed VH of Winfunc Research.
The module implements load-balancing algorithm based on least average
response header/last_byte time and least number of active connections.
The optional "inflight" mode enables accounting of incomplete
requests/sessions. This allows to mitigate cases when an upstream
server hangs and does not close connections.
Example configuration:
upstream u {
least_time header | last_byte [inflight];
server a;
server b;
}
Co-authored-by: Roman Arutyunyan <arut@nginx.com>
This restores a long-standing optimization when the entire request
body is empty and r->request_body_in_file_only is set, used to avoid
writing an empty file as initially introduced in 4c7f51136 (0.4.4).
The previous condition never worked with chunked body filter, where
rb->bufs holds at least the final chunk; in length body filter, it is
used to indicate the last received buffer since 2a7092138 (1.21.2).
The fix is to additionally check if it is the only empty buffer.
Found with UndefinedBehaviorSanitizer (pointer-overflow)
Previously, the encoder stream allocated each new inserted field in the
connection pool. This memory was not freed until the end of the connection.
Now a special insert buffer is used for all inserts.
As per RFC 9113 and RFC 9114, any message containing such headers MUST be
treated as malformed.
As per RFC 9110, Section 7.6.1, the following headers are considered
connection-specific:
- Connection
- Proxy-Connection
- Keep-Alive
- TE
- Transfer-Encoding
- Upgrade
The only exception is the TE header field, which MAY be present in a
request header, but it MUST NOT contain any value other than "trailers".
When a request was retried to a new upstream after receiving 103
Early Hints from the previous one, the accumulated early_hints_length
was not reset, causing valid early hints from the next upstream to be
incorrectly rejected as "too big".
The directive limits the number of request headers accepted from clients.
While the total amount of headers is believed to be sufficiently limited
by the existing buffer size limits (client_header_buffer_size and
large_client_header_buffers), the additional limit on the number of headers
might be beneficial to better protect backend servers.
Requested by Maksim Yevmenkin.
Signed-off-by: Elijah Zupancic <e.zupancic@f5.com>
Origin: <https://freenginx.org/hg/nginx/rev/199dc0d6b05be814b5c811876c20af58cd361fea>
The issue described below was only reproducible prior to
7924a4ec6c
When parsing the `charset` parameter in the `Content-Type` header within
`ngx_http_upstream_copy_content_type`, an input such as `charset="`
resulted in an integer underflow.
In this scenario, both `p` and `last` point to the position immediately
following the opening quote. The logic to strip a trailing quote checked
`*(last - 1)` without verifying that `last > p`. This caused `last` to
be decremented to point to the opening quote itself, making `last < p`.
The subsequent length calculation `r->headers_out.charset.len = last - p`
resulted in -1, which wrapped to `SIZE_MAX` as `len` is a `size_t`. This
invalid length was later passed to `ngx_cpymem` in `ngx_http_header_filter`,
leading to an out-of-bounds memory access (detected as
`negative-size-param` by AddressSanitizer).
The fix ensures `last > p` before attempting to strip a trailing quote,
correctly resulting in a zero-length charset for malformed input.
The oss-fuzz payload that triggers this issue holds multiple 103 status
lines, and it's a sequence of 2 of those Content-Type headers that
trigger the ASAN report.
Co-authored-by: CodeMender <codemender-patching@google.com>
Fixes: https://issues.oss-fuzz.com/issues/486561029
Signed-off-by: David Korczynski <david@adalogics.com>
Previously, when alias was used in a location with Dav COPY or MOVE
enabled, and the destination URI was shorter than the alias, integer
underflow could happen in ngx_http_map_uri_to_path(), which could
result in heap buffer overwrite, followed by a possible segfault.
With some implementations of memcpy(), the segfault could be avoided
and the overwrite could result in a change of the source or destination
file names to be outside of the location root.
Reported by Calif.io in collaboration with Claude and Anthropic Research.
Previously, a 32-bit overflow could happen while validating atom entries
count. This allowed processing of an invalid atom with entrires beyond
its boundaries with reads and writes outside of the allocated mp4 buffer.
Reported by Prabhav Srinath (sprabhav7).
Previously, data validation checks did not cover the cases when the output
contained empty buffers. Such buffers are considered illegal and produce
"zero size buf in output" alerts. The change rejects the mp4 files which
produce such alerts.
Also, the change fixes possible buffer overread and overwrite that could
happen while processing empty stco and co64 atoms, as reported by
Pavel Kohout (Aisle Research) and Tim Becker.
Updates the proxy module to use HTTP/1.1 as the default protocol when
communicating with upstream servers. This change unlocks features
such as persistent connections and chunked transfer encoding. Configurations
that require HTTP/1.0 can still override the protocol explicitly.
Keepalive is now automatically enabled in the "local" mode for upstreams
defined in configuration files. Cached keepalive connections are no longer
shared between different locations referencing the same explicit upstream
unless keepalive is explicitly configured without the "local" parameter.
To disable keepalive entirely, use keepalive 0; inside the upstream block.
To allow sharing cached connections between locations, configure
keepalive <max_cached>; without the "local" parameter.