Roman Arutyunyan [Thu, 14 May 2026 14:42:18 +0000 (18:42 +0400)]
Rewrite: fix buffer overflow with overlapping captures
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 "/++++++++++++++++++++++++++++++":
Roman Arutyunyan [Thu, 14 May 2026 13:47:42 +0000 (17:47 +0400)]
Rewrite: harden escape flags control
Following 2046b45aa0c6, 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 2046b45aa0c6.
- 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 2046b45aa0c6.
Roman Arutyunyan [Sat, 25 Apr 2026 12:41:36 +0000 (16:41 +0400)]
Mail: fix session cleanup on error path
Previously, when ngx_handle_read_event() or ngx_handle_write_event()
returned an error while handling an SMTP, POP3 or IMAP session, the
released session memory could be accessed after handling the error.
Roman Arutyunyan [Sun, 26 Apr 2026 16:20:26 +0000 (20:20 +0400)]
HTTP/2: limit Content-Type and Location response header length
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.
Proxy: fix large body with proxy_set_body and HTTP/2
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.
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.
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.
Upstream: reset parsing state after invalid status line
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.
David Carlier [Sun, 12 Apr 2026 06:13:23 +0000 (07:13 +0100)]
Charset: fix buffer over-read in recode_from_utf8().
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.
Roman Arutyunyan [Thu, 30 Apr 2026 13:15:53 +0000 (17:15 +0400)]
QUIC: avoid assigning unvalidated address to new streams
Previously, when a client migrated to a new address, new QUIC streams
received this address before validation. This allowed an attacker to
create QUIC streams with a spoofed address.
Roman Arutyunyan [Tue, 21 Apr 2026 10:51:41 +0000 (14:51 +0400)]
OCSP: resolve cleanup on connection close
Previously, when a client SSL connection was terminated (typically due to a
timeout) while resolving an OCSP responder, the OCSP context was freed, but
the resolve context was not. This resulted in use-after-free on resolve
completion.
Support 407 code in "satisfy any" and "auth_delay"
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.
Andrew Clayton [Tue, 5 May 2026 18:17:59 +0000 (19:17 +0100)]
GH: update the stale PR/issue workflow
To avoid future churn give the workflow a generic name, don't operate on
pull-requests, and extend the issues stale date to 365 days and update
its message.
Proxy: fix keepalive for HTTP/2 with explicit or no body
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.
Dav: improved path validation for COPY and MOVE operations
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.
Vladimir Homutov [Fri, 28 Oct 2016 13:01:53 +0000 (16:01 +0300)]
Upstream: least_time balancer module
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;
}
Andrew Clayton [Wed, 29 Apr 2026 18:18:03 +0000 (19:18 +0100)]
Configure: fix gcc version detection in some corner cases
If the "gcc version ... " string appeared within "Configured with:", it
was picked up rather than the real gcc version string. This might then
break the configure scripts due to a malformed NGX_COMPILER macro.
The simple fix is to look for "gcc version ... " at the start of the
line, rather than anywhere within.
Suggested-by: Aleksei Bavshin <a.bavshin@nginx.com> Closes: https://github.com/nginx/nginx/issues/1278
Sergey Kandaurov [Fri, 14 Nov 2025 12:06:56 +0000 (16:06 +0400)]
Request body: restored buffered empty body special case
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)
Henry Yuan [Thu, 23 Apr 2026 21:24:24 +0000 (21:24 +0000)]
Configure: renamed the upstream sticky module option.
The module can now be disabled with the
--without-http_upstream_sticky_module option to match
the naming convention used by other upstream modules.
Deprecated the --without-http_upstream_sticky option.
Andrew Clayton [Thu, 19 Mar 2026 04:21:21 +0000 (04:21 +0000)]
Avoid undefined behaviour in ngx_pstrdup()
In the third call to ngx_pstrdup() for setting cycle->conf_param.data in
ngx_init_cycle() we would pass in a nulled ngx_str_t in the case there
was no -g command line option passed to nginx.
This would result in a
memcpy(dst, NULL, 0)
which up to and including C23 is Undefined Behaviour.
Currently Clang and GCC (in this particular case) just treat this as a
no-op, so things just happen to work.
However some undefined behaviour sanitizers will throw an error when
this is hit, e.g. Clang and the zig compiler and it's probably best not
to rely on this behaviour.
It's worth noting that the next C standard will make this (and other
NULL related operations) defined behaviour.
Vadim Zhestikov [Mon, 2 Feb 2026 22:46:00 +0000 (14:46 -0800)]
Stream: support ALPN for proxy_ssl upstream.
Added the proxy_ssl_alpn directive, which sets the list of protocols
to advertise via ALPN during upstream TLS handshakes. Each argument
is a complex value, so variables are accepted. In particular,
proxy_ssl_alpn $ssl_alpn_protocol;
inherits the protocol negotiated in the downstream TLS handshake.
When all evaluated values are empty or absent, no ALPN extension is
sent, equivalent to the directive not being set at all.
Roman Arutyunyan [Fri, 10 Apr 2026 17:42:18 +0000 (21:42 +0400)]
HTTP/3: optimize encoder stream memory usage
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.
SSL: logging level of "record layer failure" errors
The SSL_R_RECORD_LAYER_FAILURE ("record layer failure") errors are
reported by OpenSSL 3.2 or newer as the last record layer error for
various low level read errors. Further, a976e6b9e (1.23.4) caused
to always log them at the "crit" level. For example, the following
errors are observed on OpenSSL 3.2.0 - 4.0:
SSL_read() failed (SSL: error:0A000119:SSL routines::decryption failed
or bad record mac error:0A000139:SSL routines::record layer failure)
SSL_read() failed (SSL: error:1C800066:Provider routines::cipher operation
failed error:0A000119:SSL routines::decryption failed or bad record mac
error:0A000139:SSL routines::record layer failure)
SSL_read() failed (SSL: error:0A00010B:SSL routines::wrong version number
error:0A000139:SSL routines::record layer failure)
Maxim Dounin [Sun, 5 May 2024 21:07:18 +0000 (00:07 +0300)]
SSL: logging level of "invalid alert" errors.
The SSL_R_INVALID_ALERT ("invalid alert") errors are reported by OpenSSL
1.1.1 or newer if the client sends a malformed alert. These errors are
now logged at the "info" level.
Signed-off-by: Aleksei Bavshin <a.bavshin@nginx.com>
Origin: <https://freenginx.org/hg/nginx/rev/d89e0386b695>
Maxim Dounin [Sun, 5 May 2024 21:06:15 +0000 (00:06 +0300)]
SSL: logging level of all "SSL alert number N" errors.
Errors about alerts received from peers are generated by OpenSSL by adding
peer-provided alert description (from 0 to 255) to SSL_AD_REASON_OFFSET.
All such errors, including ones for unknown alerts, are now logged at the
"info" level, as these can be caused by a misbehaving client.
Signed-off-by: Aleksei Bavshin <a.bavshin@nginx.com>
Origin: <https://freenginx.org/hg/nginx/rev/f5423ee155fe>
Andrew Clayton [Wed, 1 Apr 2026 18:17:55 +0000 (19:17 +0100)]
GH: add a workflow to check for the 'version bump' commit
This checks pull-requests to make sure the 'Version bump' commit is
immediately after the last release commit/tag.
The check includes the commits in the pull-request, so if a pull-request
is adding this commit it will accept it and also if there are other
commits in the pull-request, as long as the 'Version bump' commit is
first.
David Carlier [Sun, 15 Mar 2026 15:56:01 +0000 (15:56 +0000)]
Upstream: reset early_hints_length on upstream reinit.
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".
Maxim Dounin [Thu, 23 May 2024 21:20:01 +0000 (00:20 +0300)]
Added max_headers directive.
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.
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>
CLOCK_MONOTONIC_FAST, like CLOCK_MONOTONIC_COARSE, has low accuracy. It
shows noticeable timing variation for short intervals, which is visible
in metrics like $upstream_response_time for fast upstream responses.
This change complements the work started in commit f29d7ade5.
In addition to the reasons described in f29d7ade5, the performance of
CLOCK_MONOTONIC is good enough on modern hardware when using a TSC
timecounter. This is especially true when it is accessed through a
shared page, as implemented in FreeBSD 10.0 (see git commits 869fd80fd449 and aea810386d8e for details).
Sergey Kandaurov [Tue, 10 Mar 2026 12:28:04 +0000 (16:28 +0400)]
SSL: compatibility with OpenSSL 4.0.
X509_get_issuer_name() and X509_get_subject_name() were changed to return
a const value. Since it is passed to functions with a non const argument
in older versions, the const modifier is conditionally compiled as needed.
ASN1_INTEGER was made opaque. ASN1_STRING accessors are used to preserve
the behaviour. ASN1_STRING_get0_data() compat shim is provided for OpenSSL
< 1.1.0 where it does not exist.
Sergey Kandaurov [Wed, 18 Mar 2026 12:39:37 +0000 (16:39 +0400)]
Mail: fixed clearing s->passwd in auth http requests.
Previously, it was not properly cleared retaining length as part of
authenticating with CRAM-MD5 and APOP methods that expect to receive
password in auth response. This resulted in null pointer dereference
and worker process crash in subsequent auth attempts with CRAM-MD5.
Roman Arutyunyan [Thu, 26 Feb 2026 07:52:53 +0000 (11:52 +0400)]
Mail: host validation.
Now host name resolved from client address is validated to only contain
the characters specified in RFC 1034, Section 3.5. The validation allows
to avoid injections when using the resolved host name in auth_http and
smtp proxy.
Reported by Asim Viladi Oglu Manizada, Colin Warren,
Xiao Liu (Yunnan University), Yuan Tan (UC Riverside), and
Bird Liu (Lanzhou University).
Roman Arutyunyan [Mon, 16 Mar 2026 16:13:03 +0000 (20:13 +0400)]
Dav: destination length validation for COPY and MOVE.
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.
Mp4: fixed possible integer overflow on 32-bit platforms.
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.
Roman Arutyunyan [Sat, 21 Feb 2026 08:04:36 +0000 (12:04 +0400)]
Mp4: avoid zero size buffers in output.
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.
Roman Semenov [Tue, 27 Jan 2026 20:37:11 +0000 (12:37 -0800)]
Proxy: enabled HTTP/1.1 by default for upstream connections.
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.
Roman Semenov [Mon, 23 Mar 2026 18:03:26 +0000 (11:03 -0700)]
Upstream: enabled keepalive by default for explicit upstreams.
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.
Roman Semenov [Thu, 19 Mar 2026 19:47:14 +0000 (12:47 -0700)]
Upstream keepalive: distinguish cached connections by location.
The new "local" parameter prevents sharing cached keepalive connections
between location blocks. Connections are now reused only within the same
location.
Sergey Kandaurov [Thu, 16 Oct 2025 15:22:56 +0000 (15:22 +0000)]
The "multipath" parameter of the "listen" directive.
When configured, it enables Multipath TCP support on a listen socket.
As of now it works on Linux starting with Linux 5.6 and glibc 2.32,
where it is enabled with an IPPROTO_MPTCP socket(2) protocol.
To avoid EADDRINUSE errors in bind() and listen() when transitioning
between sockets with different protocols, SO_REUSEPORT is set on both
sockets. See f7f1607bf for potential implications.
Based on previous work by Maxime Dourov and Anthony Doeraene.
David Carlier [Fri, 20 Feb 2026 05:08:09 +0000 (05:08 +0000)]
gRPC: reset buffer chains on upstream reinit.
Previously, ctx->out was not cleared in ngx_http_grpc_reinit_request(),
which could cause queued HTTP/2 control frames (SETTINGS ACK, PING ACK,
WINDOW_UPDATE) to be sent on next upstream.
Additionally, ctx->in and ctx->busy needs to be cleared to avoid similar
problems with buffered request body fixed in cd12dc4f1.
David Carlier [Thu, 19 Feb 2026 07:26:42 +0000 (07:26 +0000)]
Proxy: reset pending control frames on HTTP/2 upstream reinit.
Previously, ctx->out was not cleared in ngx_http_proxy_v2_reinit_request(),
which could cause stale HTTP/2 control frames (SETTINGS ACK, PING ACK,
WINDOW_UPDATE) queued for the old upstream connection to be sent to a new
upstream connection during a retry.
Aleksei Bavshin [Tue, 17 Feb 2026 20:02:59 +0000 (12:02 -0800)]
Sticky: fixed expiration of learned sessions after reload.
Previously, the expiration timer for learned session was not started
until a new session is created. This could lead to the sessions being
active past the expiration time.
RFC 6265 defines "Max-Age" cookie attribute in section 5.2.2.
If the "expires" option is passed to the "sticky" directive, "max-age"
attribute will appear in cookies set by the module with corresponding
value in seconds.
For the special "max" value of the "expires" option, corresponding "max-age"
attribute value will be set to 315360000 seconds (10 years, similar to
how its done in headers_filter module for the "Cache-Control" header).
In this mode, nginx "learns" which client uses which proxied server by
analyzing headers of client requests and proxied server responses.
For example, a proxied server may start sessions by issuing the "Set-Cookie"
header field to set cookie 'sid' and returning clients will bring the cookie
with the same name.
The following configuration may be used to handle this case:
upstream u1 {
server 127.0.0.1:8080;
server 127.0.0.1:8081;
Upstream: added sticky sessions support for upstreams.
Sticky sessions allow to route the same client to the same upstream server.
- upstream structures are extended to keep session-related information
- existing balancing modules are updated to provide an id of the selected
server (SID) in pc->sid, and to select the server, given it's SID.
- other balancing modules are allowed to set the pc->hint value to choose
the desired peer. The sticky module will not change the hint if it's
already set.
- the feature is enabled by default and can be disabled with the
"--without-http_upstream_sticky" switch of the configure script.
The following configuration can be used to enable sticky sessions for
supported balancing modules:
upstream u1 {
server 127.0.0.1:8080;
server 127.0.0.1:8081;
user.email [Tue, 24 Feb 2026 01:33:57 +0000 (19:33 -0600)]
QUIC: improved error handling in OpenSSL compat layer.
Previously ngx_quic_compat_create_record() could try to encrypt a TLS
record even if encryption context was missing, which resulted in a NULL
pointer dereference.
The context is created by ngx_quic_compat_set_encryption_secret() called
from the OpenSSL keylog callback. If an error occurred in that function,
the context could remain missing. This could happen under memory pressure,
if an allocation failed inside this function.
The fix is to handle errors from ngx_quic_compat_set_encryption_secret()
and set qc->error to trigger an error after SSL_do_handshake() return.
Also, a check for context is added to ngx_quic_compat_create_record()
to avoid other similar issues.
Roman Arutyunyan [Thu, 26 Feb 2026 14:36:52 +0000 (18:36 +0400)]
QUIC: worker-bound stateless reset tokens.
Previously, it was possible to obtain a stateless reset token for a
connection by routing its packet to a wrong worker. This allowed to
terminate the connection.
The fix is to bind stateless reset token to the worker number.
Roman Arutyunyan [Tue, 24 Sep 2024 10:53:45 +0000 (14:53 +0400)]
QUIC: fixed bpf compilation with newer Linux kernels.
QUIC bpf program previously used struct bpf_map_def which was
deprecated in [1] (kernel version 5.18) and removed in [2] (kernel 6.1).
New-style BTF map definitions were added in [3] (linux kernel 5.3).
Switching the program to BTF is however not necessary since nginx has
its own relocation procedure which allows referencing the real map
structure by its file descriptor allocated earlier. In particular,
bpf instruction BPF_LD_IMM64 (0x18/0x0) is substituted with instruction
BPF_LD_MAP_FD (0x18/0x1) and map_fd is stored in the imm field, see [4]
and [5] for details.
To fix compilation, struct bpf_map_def is changed to a known type (int)
and "extern" is added to indicate external linkage and reduce object
file size.