28841 Commits
Author SHA1 Message Date
Martin Mirchev a2175623a4 BUG/MINOR: httpclient/lua: raise an error on too many request headers
When a Lua httpclient request has more header values than
tune.http.maxhdr, hlua_httpclient_table_to_hdrs() pops the inner
lua_next() key and value and returns NULL. But the key and value of the
outer lua_next() loop on the headers table stay on the stack, so the
caller's lua_next() on the argument table gets the wrong key and the
request fails with a confusing error:

    runtime error: invalid key to 'next' from [C]: in method 'get'

The header values already duplicated with istdup() are also leaked.

Let's free them and raise an explicit Lua error instead, as the caller
does for the other invalid arguments:

    runtime error: too many headers (tune.http.maxhdr is 101)

The function is now marked __LJMP and its call wrapped in MAY_LJMP().

This should be backported where 2db801c63 ("BUG/MINOR: hlua: fix stack
overflow in httpclient headers conversion") was backported. Before 3.5
the function is in src/hlua.c.
2026-11-03 18:09:22 +01:00
Martin Mirchev 0c31063873 BUG/MINOR: httpclient/lua: fix stack overread when copying the headers
hlua_httpclient_table_to_hdrs() collects the header values of a Lua
httpclient request into a stack array of tune.http.maxhdr entries, then
copies hdr_num + 1 entries into the allocated result, although the
array never contains a terminator: the result is allocated with calloc()
and its last entry is set to IST_NULL right after the copy.

Since commit 2db801c63 ("BUG/MINOR: hlua: fix stack overflow in
httpclient headers conversion") hdr_num may reach tune.http.maxhdr, and
the copy then reads one entry past the end of the array. E.g. with the
default value:

    local v = {}
    for i = 1, 101 do v[i] = "v" .. i end
    hc:get{ url = "http://127.0.0.1:8080/", headers = { ["x-many"] = v } }

ASan reports a dynamic-stack-buffer-overflow READ of size 3264 (102
entries) in hlua_httpclient_table_to_hdrs(). With fewer values, the
extra entry is uninitialized stack, which is then overwritten.

Let's only copy the hdr_num collected entries.

This should be backported to all stable versions with the Lua
httpclient (2.5 and above), on top of 2db801c63 where it was
backported. Before 3.5 the function is in src/hlua.c.
2026-11-03 18:09:20 +01:00
William Lallemand 7363d51c26 BUG/MEDIUM: ssl/cli: don't unlock the certificate lock after a failed trylock
When ckch_store_load_payload() fails to acquire ckch_lock, it jumps to
the common cleanup path, which unconditionally releases the lock. This
unlocks another operation's lock and returns success because errcode is
still zero. When the actual owner subsequently unlocks, the lock state
underflows and later certificate commands remain blocked.

Return ERR_ALERT | ERR_FATAL immediately on failed lock acquisition. No
temporary resources have been allocated at this point, so cleanup is
unnecessary.

This regression was introduced in 3.4-dev11 by commit efe6c97488, which
extracted ckch_store_load_payload() from the CLI parser. This fix should
be backported to 3.4 only. Versions 3.3 and older return directly on
failed lock acquisition and are not affected.
2026-11-03 16:58:36 +00:00
Chris Staite ca42c37bcc BUG/MEDIUM: ssl/cli: reset the ongoing transaction when "commit ssl cert" is interrupted
"commit ssl cert" builds the new instances in several calls of its I/O
handler: it yields every 10 instances, and whenever the output buffer is
full. If the applet is released in between, for example because the CLI
client closed the connection, cli_release_commit_cert() frees the new
ckch_store of the transaction, but ckchs_transaction.new_ckchs,
ckchs_transaction.old_ckchs and ckchs_transaction.path still reference
it. The next "set ssl cert", "commit ssl cert", "abort ssl cert" or
"show ssl cert" on this certificate then uses the freed store, which may
crash the process or corrupt memory.

Only an admin of the CLI can trigger it, but it doesn't take much: a
certificate used by more than 10 instances (e.g. in a crt-list loaded by
several bind lines) and a client which doesn't wait for the end of the
commit are enough.

The release function is only called with a new store still attached when
the transaction is being abandoned, the success path having consumed it
in CERT_ST_INSERT, so the transaction is reset there. On the error path,
CERT_ST_FIN already reset it and doing it again is harmless.

"commit ssl ca-file" and "commit ssl crl-file" seem to follow the same
pattern in cli_release_commit_cafile() and cli_release_commit_crlfile(),
this will be addressed separately.

This should be backported to all stable branches. Before 3.4, the release
function is the same but frees ctx->new_ckchs itself instead of calling
ckch_store_update_cleanup().
2026-11-03 17:35:38 +01:00
Chris Staite a4f92a7433 BUG/MINOR: ssl/ckch: free the new instance when ckch_inst_rebuild() fails
ckch_inst_rebuild() creates the new ckch_inst of a "commit ssl cert" with
its sni_ctx and SSL_CTX, then prepares the SSL_CTX of the server or of
the bind line. When this preparation fails it returns an error to
ckch_store_rebuild_instances() which only returns -1, so the new instance,
which is not linked anywhere yet, is leaked with its SSL_CTXs, and with
everything they reference (the certificate, the chain, the key, the CA
file links).

The impact is a memory leak for each failed commit, which requires an
admin to commit a certificate that doesn't fit its bind or server lines.

Free the instance with ckch_inst_free() and reset the pointer on these
two error paths. The instance is not in any tree nor list at this point,
which ckch_inst_free() handles. The first error path, after
ckch_inst_new_load_store() or ckch_inst_new_load_srv_store(), is already
fine since these functions never leave an instance behind on failure.

This should be backported to all stable branches, the function exists
since 2.5.
2026-11-03 17:27:44 +01:00
Christopher Faulet 234a834317 BUG/MINOR: filters: Reject id-less implicit instances of multi-instance classes
An id is mandatory when a filter class allows several instances per proxy,
and this was verified when a filter-config directive was parsed. But it was
not when an implicit instance was created by flt_add_implicit_instance()
called from another directive: for such a class, an instance without id
could not be distinguished from a second one.

Like for an explicit declaration, the implicit declaration is now reported
and ignored if no id is provided for such a class. All the in-tree
directives creating implicit instances of a multi-instance class (the
cache-store rules) already pass the cache name as id, so nothing changes for
existing configurations.

The filters documentation was updated accordingly.

No backport needed.
2026-11-03 15:53:26 +01:00
Olivier Houchard 8fc09da211 BUG/MEDIUM: mux-h1: don't drop pending output on a short content-length
When a fixed-length message is truncated and output data is still pending
in the H1 output buffer, h1s_destroy() flags the connection in error
because we got less data than expected, and so h1_send() will refuse to
send the remaining data, and not everything will be transmitted.

Now, when a truncated message is detected while output data is still
pending, H1C_F_ERROR is not set immediately, we just avoid adding it to
the idle pool, and the connection will be closed later when everything
has been sent.

To release the connection once the output buffer is drained, the
condition in h1_process() that only matched delayed silent shutdowns is
generalized to any connection in CLOSING state with an empty output
buffer. This covers both silent shutdowns and the short content-length
case. The connection is still not released while a stream remains
attached, since h1_close() keeps it in CLOSING until the stream is
detached.

This should be backported where 75dd2c18dd
has been backported, up to 2.8.
2026-11-03 14:58:48 +01:00
Willy Tarreau 078e0aa87a REGTESTS: compression: check hard to compress contents and nearly full buffers
Add reg-tests/compression/output_margin_*.vtc, with a Lua service in
output_margin.lua, which sends bodies that are incompressible or barely
compressible (random bytes, bytes that cost 9 bits each with the fixed
tree, random data mixed with text, random data with short matches, runs
of 9-bit literals separated by 4-byte matches) or repetitive. The service
serves the cases of the set named in the request in order, so that the
vtest clients just loop over the same request. Each response must be
compressed with gzip, and is decompressed by the client, which checks the
gzip trailer's CRC and length, then compares the body length to the one
announced in x-len.

The tests are split into several files so that vtest runs them in
parallel, and tagged slow:
  - output_margin_{whole,buf,small,odd}.vtc send all patterns through 4 kB
    buffers, whole or by calls of 4000, 1 to 64 or 1 to 20000 bytes;
  - output_margin_full_{random,run51}.vtc use 16 kB buffers and
    tune.maxrewrite 32, so that the buffer may be almost full when the
    filter compresses it, with bodies of 16088 to 16116 bytes sent in one
    call. With the default maxrewrite, the channel always keeps far more
    room than the deflater could ever need, which is why this does not
    happen with default settings. These sizes still leave enough room for
    the deflater, so they must be compressed as well (responses of 16144
    bytes or more are not compressed at all with this setup).

Before the fixes, the last two crashed haproxy in comp_http_payload()
(htx_get_blk_type() on the NULL block returned by htx_replace_blk_value())
for all sizes from 16086 to 16118 bytes, which is the crash reported since
3.5-dev10. Each file takes about as long as a trivial test (0.25 to 0.3s
without ASAN here, 0.33 to 0.42s with ASAN).
2026-11-03 09:40:36 +01:00
Willy Tarreau 7a65e029f1 MINOR: flt-http-comp: size the compression reserve using SLZ_MAX_OVERHEAD()
htx_compression_buffer_init() reserves room for the deflater's output based
on what it would emit as stored blocks. libslz now publishes in slz.h the
room each call to slz_encode() needs beyond its input length,
SLZ_MAX_OVERHEAD(len), which includes the stored blocks, the end of the
stream, the bits queued by previous calls and the up to 40 bytes its fixed
huffman encoding may lag behind stored blocks. Let's use it so that the
reserve follows slz's own settings instead of a local estimate.

All the DATA blocks of a pass are queued by the slz backend and compressed
in a single call when the last one is flushed, so the call covers at most
the whole buffer. The reserve is thus based on the buffer size, which keeps
it constant (52 bytes for buffers up to 64 kB), plus 14 bytes for the finish
(queue, last block and gzip trailer, a flush needs 10) and 10 bytes for the
gzip header: 76 bytes for 16 kB buffers.

The previous computation is kept for zlib, which publishes no such bound,
and when linking with an older external libslz which doesn't define the
macro. In all cases the result of htx_replace_blk_value() remains checked
since commit 5ab337485 ("BUG/MEDIUM: flt-http-comp: check the block
returned by htx_replace_blk_value()"). Crafted bodies made of runs of
literals >= 144 separated by short references, as well as random data,
sent in chunks of 256 bytes to 64 kB, all decompress correctly.

This is not a bug fix, only a way to better tie haproxy to slz's settings,
and it must not be backported.
2026-11-03 09:40:36 +01:00
Willy Tarreau 00bb955ca9 MINOR: compression: pass the content type to the algorithms' init function
Now that we have slz 1.4-dev4 with support for text-aware compression
using an alternate huffman tree, let's implement support for enabling it.
In order to permit this, the algorithms' init() function now takes a new
argument, <ctype>, which is either COMP_CT_GENERIC (0) for unknown or
binary contents, or COMP_CT_TEXT (1) for text contents.

comp_prepare_compress_request() and select_compression_response_header()
set it to COMP_CT_TEXT when the Content-Type header starts with "text/",
regardless of the "compression type" lists. The config-time checks of the
algorithms use COMP_CT_GENERIC.

The slz algorithms select SLZ_CT_TEXT after their init for text contents.
libslz has no dedicated macro for the content types, but they were
introduced right after SLZ_MAX_OVERHEAD(), which is thus used to detect an
external libslz that supports them. zlib and identity ignore the argument.

On an 81 kB HTML file, the response is 22.0 kB when served as text/html
instead of 25.4-27.1 kB when served as application/octet-stream at level 1
(the size depends on how the server sends the data).
2026-11-03 09:34:10 +01:00
Willy Tarreau 280e59cb8a IMPORT: slz: update slz to version 1.4-dev4
This refreshes the slz library to 1.4-dev4, fixing a crash bug and
bringing a small number of improvements:
  - faster (vectorizable) adler32 checksum calculation for zlib
    ("deflate" token in accept-encoding)
  - small performance improvements on compression (size and speed)
  - support for a special compression scheme for text (~-15...-20%)
2026-11-03 09:33:56 +01:00
Willy Tarreau fcee2555fc OPTIM: flt-http-comp: only flush the compressor after the last DATA block
comp_http_payload() used to call the compressor's flush() after each DATA
block, which emits a synchronization point (an EOB and an empty stored
block, up to 5 bytes) and closes the current huffman block each time.
Worse, with slz, and before recent commit a0b37fb470 ("BUG/MEDIUM:
compression: fix incompatibility between compression and large-buffers"),
add_data() only queued the data and flush() is where they were compressed,
so each block was compressed on its own, without being able to reference
the previous ones, since slz keeps no context between calls.

Now flush() is only called when no other DATA block follows in this
pass, so that the client still gets everything while we're waiting for
more data. With zlib, which compresses in add_data(), the last bits are
simply kept for the next block. With slz, all DATA blocks of the pass
are queued (the first one by reference, the following ones copied into
a contiguous buffer, as add_data() already does) and compressed at once
with their whole context. The first block's data are still referenced
after the block is removed or emptied, which is fine since this does not
touch the data area, and they are copied when the second block is added.

The difference is only visible when several DATA blocks are processed
in the same pass, e.g. a 1 MB HTML response made of 256-byte chunks
shrinks from 405057 to 402030 bytes (-0.7%) at level 1.
2026-11-03 09:33:56 +01:00
Willy Tarreau 7fe515c3dd BUG/MINOR: compression: pass the configured level to slz
Since its integration in 1.6 with commit 418b8c0c4 ("MAJOR: compression:
integrate support for libslz"), the slz backend only knew levels 0 and 1,
so the init functions forced the level to 0 or 1 using !!level, and the
rate limiting code only raised the level back up to 1. Since slz 1.4-dev3
was imported in commit 32582812a ("IMPORT: slz: update slz to version
1.4-dev3"), slz supports levels 1 to 3 (higher ones behave as 3), but
tune.comp.maxlevel was still capped to 1.

Let's pass the configured level to slz's init functions, and let the rate
limiting code raise the level back up to tune.comp.maxlevel.

Note that configurations with tune.comp.maxlevel above 1 will now use the
higher slz levels, which compress better but consume more CPU.

This must be backported wherever commit 32582812a is backported.
2026-11-03 09:33:40 +01:00
Willy Tarreau a4ecfdd49e BUG/MINOR: flt-http-comp: check the block returned by htx_replace_blk_value()
comp_http_payload() replaces the value of a DATA block with the deflater's
output and directly passes the result to htx_get_next_blk(), without checking
it. htx_replace_blk_value() returns NULL when htx_prepare_blk_expansion()
finds not enough room for the new value (delta > htx_free_space()). Normally
this cannot happen, but it seldom does during development or improvements
on the compression libraries, and causes crashes that could just be turned
into errors. The decompression code already performs the test, so let's do
the same and stay safe in front of such occasional early bugs. It's not
necessary to backport this since it doesn't trigger with stable code.
2026-11-03 09:31:27 +01:00
Willy Tarreau 1e11132396 CLEANUP: tools: make inetaddr_host_lim*() usable on untrusted inputs
inetaddr_host_lim() and inetaddr_host_lim_ret() were introduced in 1.4.4
by commit d54bbdce87 ("[MINOR] add very fast IP parsing functions") as
fast parsers for some specific use cases. They expect a delimiter after
the IP address (cr/lf/nul/whatever, in fact, anything not a digit), and
this is how they're currently used. As noted by Md. Raisul Islam Mobin,
they could also be misused by external code not aware of this limitation
which by then saved a few CPU cycles per address at an era where they
were still precious, so let's test the end pointer before reading the
digit to make it more future-proof. If this speed still matters, these
ones could probably be rewritten to benefit from automatic vectorization
on SIMD-capable machines.
2026-11-03 08:57:32 +01:00
Willy Tarreau a0b37fb470 BUG/MEDIUM: compression: fix incompatibility between compression and large-buffers
The SLZ compression being stateless, it doesn't keep data between calls
and must be called with the largest blocks at once to be efficient. When
it was added, muxes were not there yet and chunks were parsed inside the
HTTP forwarding analyzer, which would call comp->add_data() for each chunk
of data. In case of small chunks, that would just not compress, so a local
buffer was added and data were temporarily stored there and effectively
compressed when calling ->flush().

But with the introduction of large buffers, it became a problem because
the locally allocated buffer can be too small if the data to be compressed
comes from a large one. One solution to this is to also pre-allocate a
large buffer, but since there needs to be one per thread and that some
users might use extremely large buffers on large systems, that could be
extremely expensive in terms of memory.

A better solution is to just simplify all of this as this complexity is
no longer needed at the era of muxes which extract chunks and make a
large HTX data block with them. That's what this patch does, and the
functions become as simple as their zlib counterparts, no longer require
a local per-thread buffer, and will be even faster thanks to the removal
of a long-unneeded extra copy.

This fix will need to be backported to 3.4 where large buffers were
introduced, maybe after a period of observation. An alternate fix in
the mean time could be to reject configs having both compression and
large buffers.
2026-11-03 08:57:32 +01:00
Frederic Lecaille 3a319e14cf BUG/MINOR: sample: realign the ungrpc converter on message boundaries
sample_conv_ungrpc() walks the GRPC messages of the body with two
variables: <pos>, the read cursor, and <grpc_left>, the bytes left.
They are supposed to describe the same position.

When protobuf_field_lookup() fails, it does not always leave <pos> at the
end of the message. For a length-delimited field it stops on the value,
after the key and the length. But the loop still subtracts the whole
message length from <grpc_left>, without moving <pos>. The cursor and the
counter then describe different parts of the input, and the next
iteration reads a "gRPC header" from the previous message's payload.

A 23-byte body shows it:

  00 0000000a 0a 08 00 00 00 00 03 08 b9 0a 00 00000003 08 8f 4e

ungrpc(1,int64) returns 1337, which is the value of the first message's
field. The second message holds the real field 1 = 9999 but is never
reached. The counter can also over-count, and skip real messages.

Save the end of the message rigght after the "grpc_left < left"
check, then set <pos> to it when the lookup fails, so the next iteration
starts on a real message boundary.

Nothing changes on the success path, nor for fully parseable bodies,
where the lookup already stops at the message end. The multi-message walk
is intended for streaming bodies and is kept.

This is not a memory-safety issue: <pos> and the counter advance by the
header together, and the lookup moves forward by at most the message
length, which is the same amount removed from the counter.

This is also not a policy bypass like a4f5eea841 ("BUG/MEDIUM: protobuf:
fix nested path bypass in field lookup"). There, a flat sibling field
could satisfy a nested path that the configuration explicitly
restricted, so the client gained a capability it did not have. Here the
client writes the whole body and can already obtain any value with a
single well-formed message. The bug only makes the proxy read a value
that is the field of no message of the body, while the backend reads
another one. Hence MINOR and not MEDIUM.

The loop was written this way when the converter was added in 2.0
(1fceee8316) and did not change since 5f33f85ce8, apart from the
unaligned access fix. Not a regression. Backport to 2.0 and above.

Thank you to Owais Lone for having reported this bug and provided this
patch.

Must be backported to all stable versions.

Reported-by: Owais Lone <thesecguy>
2026-11-03 07:56:47 +01:00
Frederic Lecaille 1f114e4c42 CLEANUP: quic: update the outdated comment of quic_tls_derive_keys()
The comment still refers to a <ctx> parameter which no longer exists and
claims that the derived key, IV and header protection key have the same
size, which the code below contradicts. Describe the actual derivation
from RFC 9001 5.1 instead.
2026-11-03 07:56:47 +01:00
Frederic Lecaille b74a23644f CLEANUP: quic: remove the unneeded quic_rx.h include from quic_tls.c
None of the declarations of haproxy/quic_rx.h is used by quic_tls.c.
Let's remove the include.
2026-11-03 07:56:47 +01:00
Frederic Lecaille 8cc86414a7 CLEANUP: quic: remove duplicated quic_cstream_new() declaration
The function prototype is declared twice in include/haproxy/quic_conn.h.
Let's remove the second one.
2026-11-03 07:56:47 +01:00
Frederic Lecaille 3e59bbb088 CLEANUP: quic: remove the stale initial_salt declaration
No symbol named initial_salt exists anymore: the Initial salts are the
per-version initial_salt_draft_29, initial_salt_v1 and initial_salt_v2,
reached through the initial_salt member of struct quic_version. Let's
remove the leftover declaration.
2026-11-03 07:56:46 +01:00
Frederic Lecaille 54b6c4a353 CLEANUP: quic: remove the unused count and flags from struct quic_tls_kp
The count and flags members of struct quic_tls_kp are only initialized by
quic_tls_kp_init() and read nowhere: the key phase state is kept in the pn
member of the TLS secrets and in the flags member of the TLS context.
Let's remove them.
2026-11-03 07:56:46 +01:00
Frederic Lecaille 2902a959f9 CLEANUP: quic: remove the unused key parameter from quic_tls_decrypt()
The key parameter of quic_tls_decrypt() and quic_tls_decrypt2() is never
read: the key is installed in the cipher context by
quic_tls_rx_ctx_init(), only the IV differs per packet. Dead since the
cipher contexts became reusable (commit f4605748f).
2026-11-03 07:56:46 +01:00
Frederic Lecaille 528b4bc2a4 BUG/MINOR: quic: fix the header protection key length dump with AWS-LC
Under QUIC_AEAD_API, tls_hp() returns EVP_CIPHER_CHACHA20, which is an
EVP_AEAD cast to EVP_CIPHER. Applied to it, EVP_CIPHER_key_length() reads
an unrelated field and returns 1 instead of 32, so
quic_tls_keys_hexdump() only dumps one byte of the header protection key.
quic_tls_derive_keys() has the same computation but is guarded by
 #ifdef QUIC_AEAD_API. Move that special case to a helper in quic_tls.h
and use it for both.

This only affects the trace output. Measured on AWS-LC 5.9.0: 1 instead
of 32 for ChaCha20, AES-128 and AES-256 unaffected.

Should be backported to all stable versions even if only the QUIC traces
are impacted.
2026-11-03 07:56:46 +01:00
Frederic Lecaille 32f94a38fa CLEANUP: quic: remove unused tx crypto size and offset counters
The sz and offset members of the tx crypto substructure of
quic_enc_level are written but never read: the buffered CRYPTO data are
located with the bufs array and the sz member of each quic_crypto_buf.
Let's remove them.
2026-11-03 07:56:46 +01:00
Frederic Lecaille e445b1ab03 CLEANUP: quic: remove the unused tx part of struct quic_cstream
struct quic_cstream carries a tx substructure with offset, sent_offset
and buf members, inherited from the qcs object it was derived from to
handle CRYPTO streams. Nothing ever reads them: the TX side of the
CRYPTO stream is handled by the tx crypto buffers of its encryption
level for the data, and by the stream descriptor attached to the
cstream. Only quic_cstream_new() sets the three members and no reader
exists anywhere in src/ or include/.

They were already unread when they were introduced (commit 7e3f7c47e
"MINOR: quic: New quic_cstream object implementation"), so let's remove
them along with their initialization.
2026-11-03 07:56:46 +01:00
Frederic Lecaille 213e2b400f CLEANUP: quic: remove duplicated rx offset initialization in quic_cstream_new()
<cs->rx.offset> is set to 0 twice in a row. Let's drop the second useless
assignment.
2026-11-03 07:56:46 +01:00
Frederic Lecaille 461ea71745 MINOR: quic: use the right trace event in quic_tls_key_update()
The next RX/TX secret lengths are reported with QUIC_EV_CONN_SPPKTS,
whose handler expects a <struct quic_tx_packet *>, while the rest of the
function uses QUIC_EV_CONN_KP event. TRACE_PRINTF() bypasses the source
callback through <&trace_no_cb>, so nothing crashed: the message was
just filtered under the wrong event.
2026-11-03 07:56:46 +01:00
Frederic Lecaille 1f3f6b765f CLEANUP: quic: remove duplicated key phase pointer assignments
<nxt_rx> and <nxt_tx> are already initialized from <qc->ku> at
declaration time in <quic_tls_key_update()>, and <qc->ku> is not
modified in between, so the two assignments placed after TRACE_ENTER()
are dead duplicates.
2026-11-03 07:56:46 +01:00
Aleksandar LazicandClaude Opus 5.5 096b183f3e BUG/MEDIUM: leastconn: reset the smallest key when backups take over
Since commit 9fe72bba3 ("MAJOR: leastconn; Revamp the way servers are
ordered."), the lookup starts at the smallest key recorded in
lbprm.lb_seq, and this key is shared by the active and the backup
trees. Under load it follows the keys of the active servers.

When the last active server goes down or into maintenance, the key is
only reset if it equals the key of that server, which is often not the
case under load. With "option allbackups" the backup tree is then
looked up from that key, and the idle backup servers, whose key is 0,
are not found. fwlc_get_next_server() returns NULL, the streams are
queued in the backend, and without maxconn nothing dequeues them: every
request ends with a 503 after "timeout queue", although a backup server
is up. It only recovers when a server is queued at a lower key again.
Without "option allbackups" the first backup server is used directly
and the problem does not happen.

Fix it by resetting the smallest key when the last active server leaves
the tree.

This fixes GitHub issue #3555. It can be reproduced with an active and
a backup server, "balance leastconn" and "option allbackups", by
setting the active server to maintenance under load.

This should be backported as far as 3.2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Aleksandar Lazic <al-haproxy@none.at>
2026-11-02 22:26:23 +01:00
Christopher Faulet 92f34653b4 MINOR: http-act: Add 'no-fail' option on 'wait-for-body' action
With this option, the "wait-for-body" action does not return an error at the
end of the timeout but continues the message analysis with the body received
so far, instead of responding with a 408 "Request Timeout" error to the
client on the request side or a 504 "Gateway Timeout" error on the response
side. The remaining body bytes, if any, are streamed to the server or to the
client once received.
2026-11-02 21:25:24 +01:00
Christopher Faulet 3359100f56 DOC: configuration: Clarify wait-for-body doc for the response side
The documentation was too request-oriented and did not mention the 504
"Gateway Timeout" error returned to the client when the timeout occurs while
waiting for a response body. It is now side-neutral and explicitly mentions
the 408 and the 504 errors returned on timeout, depending on the side.
2026-11-02 21:25:24 +01:00
Willy Tarreau 7568a05558 BUG/MINOR: http-ana: fix response compression for applets
In 3.5-dev6, commit 1a99a7c0f4 ("MINOR: flt-http-comp: Check request
headers for response compression earlier") was needed to make sure
that processing of the accept-encoding request header happened in the
suitable order when both compression offload and decompression are
used at the same time. While theoretically it's no longer needed now
with the updated filter sequences merged into -dev9, it still uncovered
an interesting vestigal construct that we could carbon-14 date to 2014
in 1.5-dev24, precisely with this commit: 70730dddbd ("MEDIUM: http:
enable analysers to have keep-alive on stats").

By then, applets were not yet autonomous objects outside of the stream,
but were entirely part of the stream and were not much compatible with
analyzer processing, with a few exceptions, and the stream (by then
called a session) was reset to a pre-parsing state after each response
to process the next one, resulting in great difficulties explaining
why only select analyzers were kept and the other ones dropped.

In 3.5 all of this is old history and it's a bit surprising and even
refreshing to find living witnesses of this antique era, having managed
to both adapt to and resist natural evolution.

What happened with the 3.5-dev6 patch above is that function
select_compression_request_header() is no longer called by the
compression code during requests, and is expected to be called by the
rest of the natural request processing flow. But the antique code that
only keeps a subset of analyzers didn't care about ANA_REQ_HTTP_INNER
by then since applets didn't need that, and since it's the only place
which can still make sure that this function is called, the result
is that the accept-encoding header is no longer inspected for applets,
because either it was a frontend and the flag was already missing, or
it was a backend and that flag which was set for the backend was reset
by that code. Because of this, compression of applet responses (stats,
cache, promex, Lua services) is silently disabled since 3.5-dev6.

Upon careful inspecting, it turns out that all flags that were added
by the antique code (AN_REQ_FLT_HTTP_HDRS, AN_REQ_HTTP_XFER_BODY etc)
are all properly set by http_process_common() which is called on
AN_REQ_HTTP_INNER. Thus the fix consists in removing all that old
code to stop purging flags, and only enabling AN_REQ_HTTP_INNER when
applets are branched from frontends, so that the flag is always set,
and that the rest of the processing is properly performed in both
cases.

With this patch, sending curl --compressed to a config with only
a frontend or only a backend containing "stats uri /" properly
returns compressed contents again.

No backport is needed since it only affects 3.5-dev6 (3.4 and older
still work fine thanks to the code that the patch above dropped).

Incidentally, regtest legacy-implicit-order.vtc needed to be fixed
because it was written after the change above, when the cache's
output wasn't compressed by the existing rules. Now with the fix,
the cache's output gets compressed again, so adding a separate
proxy for this allows to distinguish the request processing.
2026-11-02 18:16:06 +01:00
Amaury Denoyelle 5d870741a8 BUG/MEDIUM: proxy: fix stick table expired entries cleanup on stopping
Since the following patch, proxy internal management task is only
created for frontends or if a stick-table is present. This notably
serves to purge expired entries from tables on soft-stop.

  a4a17eb366
  OPTIM/MINOR: proxy: do not init proxy management task if unused

However, the condition was incorrectly written, as it is never true for
the stick-table case. This is because table's <current> member is always
zero at parse time as the stick-table is still empty. Thus, the task is
not allocated for backends even if stick-table is declared. This results
in worker processes potentially leaving expired entries in stick-tables.

The error originates from reusing the condition from manage_proxy(),
which performs the stick-table cleanup. In this context it makes sense
to check <current> value.

Fix the condition to ensure the task is allocated when a stick-table is
declared. Checking the proxy's <table> member is sufficient for that.

This fixes github issue #3554.

This must be backported up to 3.4.
2026-11-02 14:58:15 +01:00
William Lallemand 6a5de2fdce CI: github: cache VTest binary in FreeBSD workflow
The FreeBSD job rebuilds VTest2 from source on every run. The shared
setup-vtest action cannot be used directly because it runs on the Ubuntu
host and would produce a Linux binary rather than a FreeBSD one.

Restore and save the workspace-relative vtest directory on the host with
actions/cache. Build into that directory inside the guest and explicitly
enable copyback so the resulting FreeBSD binary is available to the save
step after the VM exits.

Use the same release and architecture variables for the VM and cache key.
Keep the FreeBSD namespace separate from other jobs, and include the
upstream VTest SHA, build-script hash and a manual version suffix to
invalidate incompatible entries. Skip both cache steps if the SHA lookup
returns no output.

Probe the cached binary with -V and rebuild if it is missing or unusable.
Print the selected version and pass its absolute path to the full
regression suite. Save only after the VM step succeeds and the restore
was not an exact hit; retain the existing build and failure diagnostics.
2026-11-02 14:51:23 +01:00
William Lallemand 328547c9d8 BUILD: github: run FreeBSD CI on every branch
The FreeBSD workflow was the only workflow in .github/workflows to
restrict itself to the master and next branches. Pull request branches,
merged topic branches and tags were therefore never built or tested on
FreeBSD, unlike macOS and Alpine, which are part of the push-triggered
VTest matrix. As a result changes touching platform specific code could
be merged without any FreeBSD validation.

Drop the branches filter so the workflow triggers on every push, as the
VTest, Windows and Contrib workflows already do. The existing job
condition still limits the run to the haproxy repository, or to a manual
workflow_dispatch, so forks are unaffected.
2026-11-02 13:43:32 +01:00
William Lallemand d572a131a7 CI: github: add AWS-LC FIPS testing workflow
The FIPS branches of AWS-LC were covered by the VTest matrix, but only
built in non-FIPS mode: nothing exercised the FIPS-specific code paths in
HAProxy, in particular the compliance checks in src/fips.c which reject
non-approved ciphers, curves, signature algorithms and TLS versions at
configuration time.  A regression there is invisible until someone builds
HAProxy against an AWS-LC FIPS library.

Add a nightly workflow that builds AWS-LC with FIPS=1 and runs the VTest
and unit test suites against it, in the same way the other SSL library
workflows do.  It tests three sources, resolved through
determine_aws_lc():

  - AWS-LC 3.x, the fips-2024-09-27 branch;
  - AWS-LC 4.x, the fips-2025-09-12-lts branch;
  - the latest AWS-LC release tag, whose FIPS module is the one currently
    submitted for validation, so that the newest code is covered too
    instead of only the two maintenance branches.

Branch specs resolve to the head commit of the branch, which is passed as
AWS_LC_VERSION and downloaded as an immutable archive by build-ssl.sh, so
a run is reproducible and the build cache is only renewed when the branch
advances.  The resolved release and commit are both part of the cache
key, matching the version-specific identity used by the VTest job so the
two workflows never share a half-populated cache entry.

The workflow only runs on the upstream repository, or on manual dispatch
from a fork, like the other SSL library workflows.

Note that the AWS-LC latest release is not expected to be identical to
either FIPS branch; it is built in FIPS mode for its own FIPS module
version, which is what the extra job is meant to detect early.
2026-11-02 08:44:51 +00:00
William Lallemand 846930d20d REGTESTS: ssl: skip AWS-LC FIPS rejected crypto in ciphersuites/curves tests
Four reg-tests deliberately configure cryptographic material that
HAProxy's FIPS compliance checks reject when the SSL library runs in FIPS
mode, so they can never start a configuration there:

  - ssl_ciphersuites.vtc and quic/ssl_ciphersuites.vtc configure the
    TLS_CHACHA20_POLY1305_SHA256 ciphersuite, which is not FIPS-approved;
  - ssl_curves_selection.vtc, quic/ssl_curves_selection.vtc and
    ssl_curve_name.vtc, quic/ssl_curve_name.vtc configure the X25519
    group, which is likewise rejected.

Each then fails while parsing the configuration rather than on the
behaviour under test, which is why ssl_fips_check_ciphersuites(),
ssl_fips_check_curves() and their callers in src/ssl_sock.c exist in the
first place; the rejection is the expected outcome of the check, not a
defect in the test.

Skip those files with the fips_mode() config condition predicate already
used elsewhere in reg-tests/ssl (see "REGTESTS: ssl: skip tests broken by
FIPS mode"), so that they keep running against every non-FIPS SSL library
including OpenSSL and plain AWS-LC, and are only skipped when FIPS is
actually active.
2026-11-02 09:38:07 +01:00
Chris Staite 15c8469518 BUG/MEDIUM: ssl: skip TLS 1.3 suites in the FIPS TLS 1.2 cipher check
With AWS-LC in FIPS mode, ssl_fips_check_ciphers() walks the list returned
by SSL_CTX_get_ciphers() and rejects any entry whose key exchange is not
ECDHE (or DHE). But AWS-LC documents that this list includes the TLS 1.3
cipher suites as well, and for these SSL_CIPHER_get_kx_nid() returns
NID_kx_any since TLS 1.3 suites do not specify the key exchange. Every
TLS 1.3 suite is thus reported as a non-FIPS cipher, including the
FIPS-approved ones that are set by default:

  FIPS mode active but non-FIPS cipher(s) configured:
  'TLS_AES_128_GCM_SHA256', 'TLS_AES_256_GCM_SHA384'.

Since the internal httpclient server is checked as well, HAProxy refuses
to start in FIPS mode with any configuration, even one without a single
SSL setting. This was observed with AWS-LC FIPS 4.0.0.

TLS 1.3 suites are already validated against their own list by
ssl_fips_check_ciphersuites(), so ssl_fips_check_ciphers() must only
consider TLS 1.2 ciphers. Skip the ciphers whose minimum protocol version
is TLS 1.3 or above. SSL_CIPHER_get_min_version() was preferred over
testing for NID_kx_any because it expresses the intent directly rather
than relying on a side effect of how TLS 1.3 suites are described. Non-FIPS
TLS 1.2 ciphers (e.g. AES128-SHA or ECDHE-RSA-CHACHA20-POLY1305) and
non-FIPS TLS 1.3 suites (e.g. TLS_CHACHA20_POLY1305_SHA256) are still
rejected.

This bug was introduced in 3.5-dev3 by commit 9e2809508 ("MEDIUM: ssl: add
FIPS TLS 1.2 cipher check for AWS-LC"), no backport is needed.
2026-11-02 09:29:46 +01:00
William Lallemand 38e65c8121 BUILD: Makefile: allow external makefiles to provide build targets
Let EXTRA_MAKE fragments set EXTRA_TARGET_BUILD to a nonempty value to
provide their own build and cleanup targets instead of building HAProxy.
Keep the common option handling and regression-test targets available.

Evaluate the selector after global compiler and linker options have been
initialized so a fragment can install its alternate build rules there.
2026-10-30 16:44:33 +01:00
Amaury Denoyelle 21a17a5c4a BUG/MINOR: debug: fix "debug dev fd" crash on unknown address families
"debug dev fd" formats the local and peer addresses of each fd with
sa2str() and then replaces non-printable characters in the result. It
returns NULL for address families other than AF_INET, AF_INET6, AF_UNIX
and some AF_CUST_*. For supported address families, it can also return
NULL in case of a memory allocation failure.

Only an admin in expert mode can run the command.

Let's skip the cleanup and print "?" when sa2str() fails.

This fixes GitHub issue #3546.

This must be backported to all stable versions. In fact, strlen()
without guard was introduced in 3.1, which guarantees a crash in case of
an unsupported address. For older releases, only an unsafe
chunk_appendf() is present which may cause issues.
2026-10-30 14:04:04 +01:00
William Lallemand a9e333140c MINOR: thread: add EXTRAS_LOCK so addons can name their locks
Provide an unconditional EXTRAS_LOCK label so addons can name their
locks using the existing lock debugging and statistics infrastructure.
This avoids adding addon-specific lock labels or build-option guards to
the core enum. Report the label as EXTRAS in lock statistics.
2026-10-30 12:02:13 +01:00
William Lallemand 995295edc9 BUILD: Makefile: extend the capabilities of EXTRA_MAKE
Allow external Makefile.mk loaded through EXTRA_MAKE to extend the main
build without adding addon-specific logic to the root Makefile.

Provide the following extension points:

  - EXTRA_USE_OPTS registers additional USE_* switches. Check unknown
    options after loading the fragments so ERR=1 accepts these switches.
  - EXTRA_CFLAGS and EXTRA_LDFLAGS append compiler and linker flags.
  - EXTRA_INCLUDES adds headers to the build dependencies so changes to
    addon headers trigger recompilation.
  - EXTRA_LATE_HOOK evaluates addon logic after the object lists and
    compiler options have been collected, for example to generate
    metadata from the final build options.
  - EXTRA_INSTALL and EXTRA_UNINSTALL add targets to the install and
    uninstall prerequisites.

Reuse the existing EXTRA_OBJS variable for objects supplied by addons.
Keep all as the default goal so rules in an external fragment do not
change what a plain make invocation builds.
2026-10-30 11:23:35 +01:00
Martin Mirchev b02f93771f BUG/MINOR: sample: fix NULL dereference in the ltime/utime converters
conv_time_common() returns NULL when alloc_trash_chunk() fails, and when
the format string expanded for "%N" does not fit in the trash chunk. Its
four callers, sample_conv_us_ltime(), sample_conv_ms_ltime(),
sample_conv_us_utime() and sample_conv_ms_utime(), dereference the
result without checking it.

A configuration using one of these converters with a long format, e.g.
ms_ltime("%N%N%N...") expanding to more than bufsize, is accepted by
"haproxy -c", and the first request evaluating it crashes the process
with a NULL dereference. A failed allocation at runtime has the same
effect with an ordinary format.

Let's make the converter fail instead, like on its other error paths.

This fixes GitHub issue #3545.

This must be backported to all stable versions up to 3.0, as commit
5e63e1636 introduced the first version of conv_time_common() with a
possible NULL return.
2026-10-30 10:33:13 +01:00
William Lallemand e5e52e33c9 BUG/MINOR: errors: reset diagnostic buffers after thread cleanup
A live worker frees its thread-local diagnostic buffers before EXTRA_OBJS
teardown, whose deinitializers can still emit diagnostics.

ha_free() releases the allocation and clears only the area pointer. It
leaves size, head and data unchanged, while b_is_null() tests size rather
than area. A previously allocated buffer therefore still looks allocated
with a NULL area. Later diagnostics skip lazy allocation and can crash
when accessing the buffer.

Reset both usermsgs_buf and usermsgs_ctx.str to BUF_NULL after freeing
their areas. This clears the stale size and contents metadata so later
diagnostics can allocate fresh buffers. The post-deinit cleanup releases
any buffers reallocated during EXTRA_OBJS teardown.

This could be backported in every maintained versions.
2026-10-29 17:43:04 +00:00
Olivier Houchard e596dcdca8 MINOR: sym: use the symbol table to resolve a DSO name
resolve_dso_name() only used dladdr() to find the object containing an
address. Our in-process symbol table also knows the owning object, is
lock-free and async-signal-safe, and also works on static builds where
dladdr() may not. Try it first and keep dladdr() as a fallback for
addresses that are not covered by any symbol, or when the table has not
been built yet.
2026-10-29 15:18:35 +01:00
Olivier Houchard 030ae75ff8 CLEANUP: sym: factor out the short object name extraction
dump_sym_lookup() and resolve_dso_name() both keep the part of an object's
path between the last '/' and the first following '.'. Introduce
append_obj_name() for it and use it in both places.
2026-10-29 15:18:35 +01:00
Aurelien DARRAGON 1d84ea775a BUG/MINOR: checks/log: force implicit UDP checks as TCP for log backend
As described in GH #3528, declaring an UDP log server with "check"
keyword, without specifying check port would result in haproxy
starting a check using TCP oriented API while the underlying address
family was implicit datagram. It would either cause the check to
always fail, or even segfault on some systems where the network API
from the operating system doesn't expect DGRAM family with stream oriented
functions such as "connect()".

To fix the issue, although we do support UDP log endpoints, there is no
UDP checks, and we don't offer ICMP checks, so let's just fallback to
TCP protocol when we detect the address family is DGRAM when starting
a check from a log backend.

The documentation was updated to describe this pitfall.

Many thanks to GH user @ssavant2 for filing a bug report and providing
useful reproducer.

It may be backported to 3.0.
2026-10-29 13:58:25 +01:00
Olivier Houchard 463527ba1e BUILD: sym: Fix build on recent glibc
With a recent glibc, as found on Ubuntu 26.04, with the default locale,
sort and comm do not always agree on the ordering of all characters,
notably punctuation characters, and comm will thus fail when reading the
output of sort, producing a warning, and prevent the minidebug section
from being created.
Just force them to use the C locale, where this is not a problem.
2026-10-28 16:54:16 +01:00
Willy Tarreau 44b2c8ae32 [RELEASE] Released version 3.5-dev10
Released version 3.5-dev10 with the following main changes :
    - BUG/MEDIUM: stats-file: shm-stats-file be counters not working anymore
    - BUG/MINOR: quic: rearm packet on packet received even non ack-eliciting
    - REGTESTS: use HAPROXY_TEST_TIMEOUT for wait in purge srv/be
    - BUILD: sym: Make sure minidebug works correctly with most environments
    - BUILD: sym: Use dl_iterate_phdr() for more OSes.
    - BUILD: sym: Get it to build and work on various OSes
    - BUILD: sym: Make sure __always_inline always is inline.
    - BUILD: sym: Build properly with gold as a linker
    - BUILD: sym: Keep a temporary binary without .gnu_debugdata
    - BUILD: sym: Don't add a minidebug section to haterm and haload
    - MINOR: quic: use the listener for Retry and Version Negotiation emission
    - MINOR: quic: define a function to emit a datagram on a listener socket
    - BUG/MEDIUM: quic: use datagram destination as source for Retry
    - BUG/MINOR: quic: use datagram destination as source for stateless reset
    - BUG/MINOR: quic: use datagram destination as source for version negotiation
    - REGTESTS: quic: check Retry source address on a wildcard listener
    - BUG/MINOR: quic: handle EINTR on emission of packets without connection
    - MINOR: quic: rename confusing qc_purge_txbuf() to qc_flush_txbuf()
    - BUG/MEDIUM: mux_quic: prevent uaf on fctl frame after send error
    - BUG/MINOR: mux_quic: prevent RS/SS on unistream with hq-interop
    - BUG/MINOR: check: do not inspect errno if conn FDLESS
    - BUG/MINOR: mux_quic: do not crash on SE_SHR_DRAIN shut
    - MEDIUM: quic: implement the AEAD confidentiality limit
    - OPTIM: quic: use native HKDF APIs where available
    - TESTS: quic: add HKDF unit tests using RFC 5869 vectors
    - BUG/MEDIUM: peers: keep the earliest sync task deadline
    - DOC: config: explain per ACL line limitations
    - MINOR: quic: use the same HP encryption helpers for RX and TX
    - BUG/MINOR: quic: valid NEW_TOKEN connections are counted as half-open
    - BUG/MEDIUM: cli: Fix parsing of big payloads with dynamically allocated buffer
    - BUG/MINOR: filters: fix silent compression disable with legacy "filter compression" first
    - MINOR: filters: Support the global side in filter_place_class()
    - OPTIMIZE: stream: Stop holding a reference on the frontend during stream_free()
    - BUG/MEDIUM: mux-h1: Don't close the connection on a payload parsing error
    - BUG/MEDIUM: h1: make the Connection/Upgrade headers cleanup idempotent
    - BUG/MINOR: h1: keep accepting unsafe violations after a parser reset
    - BUG/MEDIUM: h1: don't rewrite the Upgrade header when not cleaning headers
    - BUG/MEDIUM: h1-htx: don't stall on messages ending exactly at the buffer's end
    - BUG/MEDIUM: mux-h1: don't truncate the response when closing after draining
    - BUG/MEDIUM: mux-h1: don't truncate responses on soft-stop
    - BUG/MINOR: h1: don't use uninitialized start line pointers when restarting
    - BUG/MINOR: mux-h1: only reset the session's timing info on the frontend side
    - BUG/MINOR: mux-h1: fix the open streams counter on stream creation failure
    - BUG/MINOR: mux-h1: wake the connection up when a pending error can be sent
    - BUG/MINOR: mux-h1: don't dereference a NULL opposite sedesc in h1_fastfwd()
    - BUG/MEDIUM: h1: reject CONNECT request targets other than "host:port"
    - BUG/MEDIUM: mux-h1: don't lose the end of the previous chunk in h1_make_chunk()
    - BUG/MEDIUM: mux-h1: don't stall when draining more than a buffer of data
    - BUG/MINOR: mux-h1: reset the content-length/chunking state after a 1xx response
    - BUG/MINOR: mux-h1: don't keep fast-forwarding disabled for the next streams
    - BUG/MINOR: mux-h1: don't report a 408 error on timeout of a closed connection
    - BUG/MEDIUM: mux-h1: don't swap buffers when transferring data with a reserved offset
    - BUG/MINOR: mux-h1: report payload parsing glitches to the session
    - BUG/MEDIUM: mux-h1: don't lose error responses once a content-length was parsed
    - BUG/MEDIUM: h1: build a missing Host header from the authority of the URI
    - BUG/MINOR: h1: reject CONNECT authorities with an empty host
    - BUG/MINOR: h1: forget a previous pass' results when parsing headers only
    - BUG/MEDIUM: mux-h1: don't send chunked responses to HTTP/1.0 clients
    - BUG/MINOR: mux-h1: convert the <from> argument of h1-case-adjust to lower case
    - BUG/MEDIUM: h1: reject absolute http(s) URIs with an empty host
    - BUG/MINOR: mux-h1: only rely on the client's method for bodyless responses
    - BUG/MINOR: mux-h1: search the websocket key in the right message
    - BUG/MINOR: mux-h1: don't forward request trailers named like server name header
    - BUG/MEDIUM: mux-h1: don't spread-close idle conns with idle-close-on-response
    - BUG/MINOR: mux-h1: Don't subscribe for receives once read0 or error received
    - BUILD: makefile: better support git worktrees
    - BUG/MINOR: sym: don't crash on images linked at a fixed base
    - CI: github: run several weekly/monthly jobs daily
    - DOC: remove the AWS-LC CI image
    - BUG/MEDIUM: resolvers: reject too long labels when encoding a name
    - CI: github: fix VTest cache paths in container jobs
    - BUG/MAJOR: mux-quic: do not crash on connection error after glitch threshold
    - BUG/MAJOR: mux-h2: split absolute URIs like the HTTP analysers do on output
    - BUG/MEDIUM: h3: split absolute URIs like the HTTP analysers do on output
    - BUG/MINOR: filters: free the error message of the filter-config parsers
    - MINOR: filters: initialize the filter classes before the register stage
    - BUG/MEDIUM: bwlim: never burst more than the pending data in input
    - MINOR: filters: Move cache-store after decompression in global classes list
    - BUG/MINOR: sample: Fix wrong 'base64urldec' return value check in 'jwt_member_query'
    - BUG/MEDIUM: h3: fix BUG_ON() crash with glitch on SETTINGS parsing
    - BUG/MEDIUM: h3: fix BUG_ON() crash with glitch on GOAWAY parsing
    - BUG/MEDIUM: h3: fix BUG_ON() crash with glitch after QPACK dec err
    - MINOR: h3: remove dead code on h3_rcv_buf() error path
    - BUG/MINOR: h3: fix risk of BUG_ON() crash with glitch on unknown frame
    - BUG/MINOR: h3: fix BUG_ON() crash with glitch on htx_set_eom() failure
    - REGTESTS: filters: Fix the global filters order as reported on the CLI
    - Revert "BUG/MAJOR: mux-quic: do not crash on connection error after glitch threshold"
    - MEDIUM: filters: automatically order the implicit core filters in legacy mode
    - DOC: filters: document the automatic ordering of the legacy core filters
    - MEDIUM: filters: eval the backend instances first on the response path
    - MEDIUM: filters: eval the legacy backend filters first on the response path
    - BUG/MINOR: flt-decomp: Report error about filter order in lagacy mode only
    - BUG/MEDIUM: h3: fix memory corruption on trailers parsing realign
    - BUG/MINOR: mux_quic: wake streams on soft-stop using listener socket
    - BUG/MINOR: h3: enforce maxrewrite on request headers
    - BUG/MINOR: h3: fix crash with maxrewrite on response headers
    - BUG/MINOR: cfgparse/peers: warn and prevent undefined behavior when peers is missing bind info
    - BUG/MEDIUM: task: do not destroy twice the tasks already killed in mworker_cleantasks()
    - BUG/MEDIUM: mux_quic: fix crash on QCS double remote close
    - BUG/MEDIUM: mux_quic: ensure FIN is reported after Rx data transfer
    - MINOR: thread: split out thread_bind_to_cpu_map()
    - OPTIM: fd: Make each thread group's FD table NUMA-local
    - OPTIM: fd: make each thread group's polled_mask NUMA-local
    - MINOR: ssl: Add new 'pha' server option
    - BUILD: thread: fix builds without thread support
    - DOC: config: add missing acme.scheduler in the global index
    - MINOR: tools: add find_word_index_in_array() to find a keyword
    - MINOR: http/config: add acceptable-http-re{quest,sponse}-violations
    - MINOR: http: add one control per existing HTTP violation
    - MEDIUM: http: add the "short-status-code" violation exception
    - BUG/MINOR: jwt: don't truncate binary HMAC secrets at the first NULL byte
    - BUG/MINOR: jws: fix ECDSA_SIG leak in jws_b64_signature()
    - BUG/MINOR: jwe: only accept exact "alg" and "enc" names in JWE tokens
    - BUG/MINOR: jwe: reject non-empty encrypted keys with "dir" and "ECDH-ES"
    - BUG/MINOR: jwe: only match the exact "ALL" word in jwt.decrypt_{alg,enc}_list
    - BUG/MINOR: jwt: enforce the ECDSA signature length in jwt_verify
    - BUG/MINOR: mux-quic: prevent send corruption on buf realign
    - BUG/MEDIUM: mux_quic: fix FIN received with an already known offset
    - BUG/MINOR: quic: fix "show quic" filtered connection
    - BUG/MINOR: mux_quic: fix MAX_STREAMS limit
    - BUG/MINOR: mux_quic: fix overflow on avail_streams
    - MINOR: cli/server: speed up proxy/server purge via the CLI
    - BUG/MINOR: promex: don't loop forever when a module skips a time series
    - BUG/MINOR: http-ana: call the http_reset filter callback on L7 retries
    - DOC: filters: channel_start_analyze is called again on L7 retries
    - BUG/MEDIUM: server: Don't use the cached ALPN for websocket
    - BUG/MAJOR: h3: always check :path for all scheme values
    - BUG/MEDIUM: h3: prevent conn failure on trailers decoding without space
    - BUG/MINOR: h3: do not retry on trailers too large
    - BUG/MINOR: h3: do not wait after Tx realloc failure
    - BUG/MEDIUM: connection: don't leak the fd of an accepted connection
    - BUG/MINOR: hlua/filters: Track the payload phase per channel
    - BUG/MEDIUM: hlua/filters: make HTTPMessage.eom/may_recv relative to the filter's data
    - MINOR: hlua/filters: Add HTTPMessage.closed() to check if input is closed
    - BUG/MINOR: hlua/filters: Restrict HTTPMessage.set_eom/unset_eom to visible data
    - BUG/MEDIUM: hlua/filters: Make Channel.may_recv relative to the filter's data
    - BUG/MEDIUM: hlua/filters: Never arm the analysis timer for the lua wake-ups
    - BUG/MEDIUM: filters: Don't propagate a close while a filter waits on a channel
    - BUG/MEDIUM: deviceatlas: isolate threads while swapping the reloaded atlas
    - BUG/MINOR: deviceatlas: refresh the user-agent evidence id after a reload
    - BUG/MINOR: deviceatlas: ignore the scheduler's update in progress marker
    - BUG/MINOR: deviceatlas: use the result's property type when printing
    - BUG/MINOR: deviceatlas: consume a scheduled update before loading it
    - BUG/MEDIUM: peers: do not keep a dict entry cached when its update was not sent
    - BUG/MEDIUM: peers: fix byte order of last_get on updates without identifier
    - BUG/MINOR: peers: forget the appctx when the peer session init fails
    - BUG/MINOR: peers: don't lose the peer's version when the hello is fragmented
    - BUG/MINOR: peers: reject over-long varints in intdecode()
    - BUG/MINOR: peers: account incoming sessions in the per-thread applet counters
    - BUG/MINOR: peers: compare the received expiration date as unsigned
    - BUG/MINOR: peers: don't use remote table definitions from a previous session
    - BUG/MINOR: peers: forget the remote table when ignoring an unknown key type
    - BUG/MEDIUM: listener: enforce rate-limit sessions across thread groups
    - BUG/MINOR: hlua: unlock Patref before add_bulk type error
    - MINOR: filters: Set the class flags on filter class registration
    - MINOR: filters: Deduce the default filter API from the implied filters
    - MINOR: filters: Warn about ignored legacy filters for a proxy
    - MINOR: filters: Declare a proxy as using the legacy API before parsing filter directive
    - MINOR: filters: Add a function to remove a filter conf from all proxy lists
    - MINOR: filters: Add a function to find a legacy filter configuration during parsing
    - MINOR: cache: Stop to explicitly loop on the proxy's filter_configs list
    - MINOR: fcgi-app: Stop to explicitly loop on the proxy's filter_configs list
    - MINOR: bwlim: Stop to explicitly loop on the proxy's filter_configs list
    - MINOR: flt-decomp: Stop to explicitly loop on the proxy's filter_configs list
    - MINOR: flt-http-comp: Stop to explicitly loop on the proxy's filter_configs list
    - MINOR: filters: Document the public helpers missing from the filters guide
    - BUG/MEDIUM: applet: check buffer sizes before HTX transfer
    - DOC: config: Fix documentation about dot character in variable names
    - DOC: configuration: document library compatibility for TLS keywords
    - DOC: configuration: document library compatibility for pha
    - MINOR: filters: Pre-initialize the instances in check_config_validity()
    - BUG/MAJOR: mux_quic: remove data not yet sent on reset stream
    - BUG/MEDIUM: mux_quic: do not reset timeout after shut
    - BUG/MINOR: mux_quic: fix possible BUG_ON() after frame alloc failure
    - BUG/MINOR: mux_quic: fix ALPN retrieval from server
    - BUG/MINOR: mux_quic: fix stream elasticity dec on qcm_init() failure
    - BUG/MINOR: h3: reset HTX message after trailers parsing error
    - BUG/MINOR: thread: don't turn reversed thread ranges into "even"
    - BUG/MINOR: tools: make strl2irc() and strl2llrc() reject a lone minus sign
    - BUG/MINOR: cfgparse-listen: reject negative capture lengths
    - BUG/MINOR: stick-table: reject negative stick counter IDs in sc-* actions
    - CLEANUP: resolvers: don't close the nameserver socket twice on deinit
    - BUG/MINOR: connection: check the NetScaler CIP header length against received data
    - BUG/MINOR: connection: check the IP header length in NetScaler CIP
    - BUG/MEDIUM: ssl-gencert: fix a NULL dereference with QUIC listeners
    - BUG/MEDIUM: ssl: fix an out-of-bounds read on SSL_CTX switch with a .sctl file
    - BUG/MEDIUM: ssl: support SCTL with AWS-LC
    - BUG/MINOR: ssl: fix a double-free of the .sctl buffer with several contexts
    - BUG/MINOR: ssl: support SCTL in TLS 1.3 Certificate messages
    - DOC: configuration: clarify SCT support by TLS library and version
    - BUG/MINOR: server: free the sample expression when it is not usable on a server
    - BUG/MEDIUM: mux-spop: Don't include DALLOC/DFULL flags in SPOP_CF_DEM_BLOCK_ANY
    - DOC: config: add missing entry for health checks
    - IMPORT: slz: update slz to version 1.4-dev3
v3.5-dev10
2026-10-28 15:43:02 +01:00