]> git.kaiwu.me - haproxy.git/log
haproxy.git
6 weeks agoBUG/MINOR: payload: always report a boolean from req.ssl_ec_ext
Willy Tarreau [Wed, 30 Sep 2026 09:23:40 +0000 (11:23 +0200)]
BUG/MINOR: payload: always report a boolean from req.ssl_ec_ext

req.ssl_ec_ext is documented to return "a boolean identifying if client
sent the Supported Elliptic Curves Extension", but like a few other such
sample fetch functions, it fails to return false and incorrectly returns
a fetch failure instead. It must properly return false when the hello
parses correctly but doesn't have the EC extension instead of making the
sample fetch fail as if the hello was unparsable.

The bug was in the initial version in 1.6-dev3 with commit 5fc7d7e8ce
("MINOR: Add sample fetch to detect Supported Elliptic Curves Extension"),
so this fix can be backported to all stable branches.

6 weeks agoBUG/MINOR: stick-table: do not count the lookup itself in table_trackers()
Willy Tarreau [Wed, 30 Sep 2026 09:19:05 +0000 (11:19 +0200)]
BUG/MINOR: stick-table: do not count the lookup itself in table_trackers()

In 1.8 when adding multi-threading support with commit 819fc6f563
("MEDIUM: threads/stick-tables: handle multithreads on stick tables"),
the entry's ref_cnt was incremented by one on all trackers and accesses,
and smp_fetch_sc_trackers() learned to return one less than the visible
value so as not to count itself in it.

One of them was missed: table_trackers() returns the number of trackers
without subtracting one so it's always one above the real value. Let's
adjust it like other places do.

This can be backported to all versions.

6 weeks agoBUG/MINOR: http-ana: return a 502 when checkcache blocks a response
Willy Tarreau [Wed, 30 Sep 2026 09:03:34 +0000 (11:03 +0200)]
BUG/MINOR: http-ana: return a 502 when checkcache blocks a response

The documentation of "option checkcache" states that a response which
does not respect its requirements "will be blocked just as if it was
from an http-response deny rule, with an HTTP 502 bad gateway".

The code jumps to the "deny" label without touching txn->status, which
at that point still holds the status the server had returned. The
response delivered to the client is thus a 200 (or anything else that
the server returned), and the error files are not even used in this
case. Let's just properly set the status to 502 just like the other
rules such as "http-response deny" do. It now does what the doc says.

This has been there forever so it can be backported to all stable
branches.

6 weeks agoBUG/MINOR: sample: make quic_enabled() always succeed
Willy Tarreau [Wed, 30 Sep 2026 08:56:37 +0000 (10:56 +0200)]
BUG/MINOR: sample: make quic_enabled() always succeed

smp_fetch_quic_enabled() ends with "return smp->data.u.sint" instead of
"return 1", so it returns its boolean result as the success status of
the fetch as well. On a build without QUIC, or when the QUIC listeners
are disabled, the fetch therefore fails instead of reporting a false
boolean, and any expression using it fails to evaluate.

The issue was introduced in 2.8-dev2 with commit 33d11c464f ("MINOR:
sample: Add "quic_enabled" sample fetch") so this fix should be
backported to 2.8.

6 weeks agoBUG/MINOR: sample: zero-pad the fractional part emitted by http_date()
Willy Tarreau [Wed, 30 Sep 2026 08:49:48 +0000 (10:49 +0200)]
BUG/MINOR: sample: zero-pad the fractional part emitted by http_date()

When http_date() is given the "ms" or "us" unit, it appends the
sub-second part of the date with a plain "%d", so it is printed without
any padding: 123 ms gives ".123" but 23 ms gives ".23", which stands
for a completely different instant, and 23 us gives ".23" as well
although the two units must not produce the same string.

The fraction is now printed over the number of digits its unit
requires, three for milliseconds and six for microseconds. The test
which selects the format is also made to depend on that width rather
than on the unit argument, which is equivalent but clearer.

The bug was introduced in 2.1-dev4 with commit ae6f125c7b ("MINOR:
sample: add us/ms support to date/http_date"), so this fix should
be backported to all stable branches.

6 weeks agoBUG/MINOR: sample: apply the ms_/us_ time offsets in the input unit
Willy Tarreau [Wed, 30 Sep 2026 07:23:47 +0000 (09:23 +0200)]
BUG/MINOR: sample: apply the ms_/us_ time offsets in the input unit

The documentation of ms_ltime(), ms_utime(), us_ltime() and us_utime()
states that their optional <offset> is expressed in milliseconds or in
microseconds, that is in the same unit as their input. The four of them
however split the input into a number of seconds and a sub-second part
first, and then add the offset to the number of seconds, so the offset
was really applied in seconds and could never shift the sub-second part.

With int(1234567890123),ms_utime("%H:%M:%S.%3N",877) the result was
23:46:07.123, that is 877 seconds later, instead of the 23:31:31.000
the documentation describes.

The offset is now added to the input before it is split, which also
lets it carry the sub-second part as intended. The plain ltime() and
utime() take their offset in seconds and are unaffected.

The bug was introduced with the converters in 2.9-dev3 with commit
5e63e1636a ("MEDIUM: sample: implement us and ms variant of utime and
ltime"), so the fix must be backported as far as 3.0.

6 weeks agoBUG/MINOR: http: do not consume the delimiter of a truncated q-factor
Willy Tarreau [Wed, 30 Sep 2026 06:50:07 +0000 (08:50 +0200)]
BUG/MINOR: http: do not consume the delimiter of a truncated q-factor

http_parse_qvalue() increments its cursor before checking that the
character it just read is the '.' which must follow the integer part:

if (*qvalue++ != '.')
goto out;

so when the q-factor has no decimal part, as in "q=0", the cursor
reported through <end> points one byte past the character which
terminated it, that is past the ',' separating it from the next value.

sample_conv_q_preferred(), which implements the "language" converter,
resumes its scan at that position and then looks for the next ',' from
there, so it silently skips everything up to the following separator.
With "accept-language: fr;q=0, en;q=0.1" the "en" value is never
examined and the converter reports no match at all, while "q=0.0"
works as expected.

The other caller, the accept-encoding parsing of the compression
filter, passes NULL as <end> and is unaffected. Note that this bug
has been there since the introduction of the function in 1.5-dev23
via commit 0e9b1b4d1f ("MEDIUM: compression: consider the "q="
attribute in Accept-Encoding").

This should be backported to all branches.

6 weeks agoBUG/MINOR: startup: don't chroot by default when set-dumpable is set
Willy Tarreau [Wed, 30 Sep 2026 11:39:32 +0000 (13:39 +0200)]
BUG/MINOR: startup: don't chroot by default when set-dumpable is set

In 3.5-dev7, the automatic chroot by default was applied with commit
79082c7c2b ("MAJOR: startup: use chroot auto by default when available"),
but it has the undesired effect to prevent us from getting a core dump
by default, even when set-dumpable is set.

Let's make sure set-dumpable disables this mechanism and document it.
This is cleaner than entering a "chroot /" because set-dumpable is
clearly identified as a debugging option that should be left commented
out after use.

Note: maybe we'll need to add a "chroot /" to all reg-tests now, as it
is no longer possible to get a core dumped by default, or we may also
use a command-line option to enforce set-dumpable.

No backport is needed.

6 weeks agoBUG/MINOR: jwe: Avoid buffer overflow in fake cek (RSA1.5)
Remi Tricot-Le Breton [Wed, 30 Sep 2026 08:27:13 +0000 (10:27 +0200)]
BUG/MINOR: jwe: Avoid buffer overflow in fake cek (RSA1.5)

In commit 1a5a333 "BUG/MEDIUM: jwe: substitute random CEK on RSA1_5
decryption failure per RFC 7516 #11.5" we fill a fake CEK with random
data to avoid timing attacks but the size of the buffer was never
checked. If we happened to have a too little bufsize (less than 64
bytes) we could have out of bounds accesses.

This issue was found by Dmitri Kaslov.

This patch should be backported to 3.4.

6 weeks agoBUG/MINOR: jwe: Buffer overflow when filling fake cek in case of RSA1.5
Remi Tricot-Le Breton [Wed, 30 Sep 2026 08:27:12 +0000 (10:27 +0200)]
BUG/MINOR: jwe: Buffer overflow when filling fake cek in case of RSA1.5

In commit 1a5a333 "BUG/MEDIUM: jwe: substitute random CEK on RSA1_5
decryption failure per RFC 7516 #11.5" we fill a fake CEK with random
data to avoid timing attacks. But the loop that fills the buffer does is
8 bytes at a time but the index was not properly incremented which could
result in a buffer overflow if the "struct buffer" size was too short.

This issue was found by Dmitri Kaslov.

This patch should be backported to 3.4.

6 weeks agoBUG/MEDIUM: pattern: don't dereference static_pattern's data when not filling it
Willy Tarreau [Wed, 30 Sep 2026 14:07:21 +0000 (16:07 +0200)]
BUG/MEDIUM: pattern: don't dereference static_pattern's data when not filling it

The match functions (pat_match_str() etc) may return a pointer to the
thread-local <static_pattern> without updating it when <fill> is not
set. However pattern_exec_match() then unconditionally duplicates the
returned pattern's sample data, dereferencing static_pattern.data, which
may still hold a stale pointer left there by an earlier lookup performed
on the same thread with <fill> set but without going through
pattern_exec_match(). This is what the CLI's "get map" does in
cli_io_handler_map_lookup(): it calls the match function directly, so
static_pattern.data points to the element's real sample data. If this
element is later freed (e.g. by "del map", "clear map", or the purge of
the old generation after "commit map"), the next ACL match on this thread
succeeding via the tree (ACLs use fill=0) reads freed memory in
pattern_exec_match().

This can be reproduced with an ACL and a map, both using virtual files,
with one thread:

    acl known url_param(q) -f virt@cliacl
    http-request set-var(txn.map) url_param(q),map(virt@climap)

then on the CLI: "add map virt@climap one first", "get map virt@climap
one", "prepare map virt@climap", "prepare acl virt@cliacl", "add map @1
virt@climap two third", "add acl @1 virt@cliacl two", "commit map @1
virt@climap", "commit acl @1 virt@cliacl", and finally a request to
"/?q=two" makes ASAN report a heap-use-after-free in pattern_exec_match().
Without ASAN, random garbage may be read, or the process may crash.

The fix consists in not touching the returned pattern at all when <fill>
is not set, since the caller only checks it against NULL. The problem was
introduced in 1.8 with commit b5997f740 ("MAJOR: threads/map: Make
acls/maps thread safe") so this must be backported to all stable versions.

6 weeks agoREGTESTS: http-messaging: consume window update before txdata
William Lallemand [Wed, 30 Sep 2026 13:03:15 +0000 (13:03 +0000)]
REGTESTS: http-messaging: consume window update before txdata

From time to time, connect_aborted_inflight.vtc seems to fail with:

    ---- c1.1  Frame #0 for rxwinup was of type HEADERS (1) instead of WINDOW_UPDATE (8)

The patch consumes the windows update earlier so it doesn't end up with
this error.

6 weeks agoBUG/MEDIUM: stick-table: restrict key sizes to what fits in a buffer
Willy Tarreau [Tue, 29 Sep 2026 14:23:44 +0000 (16:23 +0200)]
BUG/MEDIUM: stick-table: restrict key sizes to what fits in a buffer

Dmitri Kaslov reported an interesting bug in the stick-table key exchange
over peers: the few extra bytes of overhead and data storage are not
accounted when building the key to be transmitted, while a message must
fit into a single buffer. This means in practise that a stick-table key
as large as a buffer can theoretically crash the sender. But it's not
that trivial since such a key has to be made (which wasn't the case in
1.4 when this was designed, rules and converters to do this weren't that
rich). For strings it doesn't happen by accident though, but nowadays
it's possible to do it by tracking too large data or even applying an
expansion converter (b64dec, hex etc). For binary it's easier because
the whole block is always padded with zeros and the whole size is sent.

It has become easier to trigger with large buffers because it's now
possible to wait for a large body and track it as a large key. During
local tests, mostly applet issues were noticed, with the peers applet
not being able to serialize its message and looping back there trying
again. But with ASAN it's visible that a few bytes are overwritten at
the end of the buffer.

In order to address this, here we apply a limit to what will be
serialized. For binary keys, that cannot be trimmed, we simply refuse
to start with too large a key + data storage for the buffer size,
explaining what to do to fix it (hint: nobody sane uses 16kB keys).
For strings, they're simply trimmed to the room left once data are
subtracted from the buffer size.

This is sufficient to stop this theoretical problem without affecting
peers transfers during an upgrade (no protocol change), nor affecting
anyone with a realistic configuration.

Thanks to Dmitri Kaslov for this report with a reproducer.

This should be backported to all stable releases, along with these two
previous commits:

  MINOR: stick-table: provide a function to estimate on-wire data size
  MINOR: stick-table: calculate the on-wire size of each key

6 weeks agoMINOR: stick-table: calculate the on-wire size of each key
Willy Tarreau [Tue, 29 Sep 2026 14:21:10 +0000 (16:21 +0200)]
MINOR: stick-table: calculate the on-wire size of each key

In stick_table->proto_size, we now store the estimated worse case for
the size of a key sent over the wire with all of its stored data. This
includes the key itself when it's fixed, the upper bound for stored
data, and some overhead (11 bytes measured). It will be used by next
commit.

6 weeks agoMINOR: stick-table: provide a function to estimate on-wire data size
Willy Tarreau [Tue, 29 Sep 2026 14:19:07 +0000 (16:19 +0200)]
MINOR: stick-table: provide a function to estimate on-wire data size

We'll need to estimate the on-wire size of the stored data as a majorant
to adjust key sizes, so let's provide a new function for this. It
considers all varint at their maximum size.

6 weeks agoBUG/MINOR: mux-h1: Discard pending raw data after a tunnel rejection
Christopher Faulet [Tue, 29 Sep 2026 12:20:26 +0000 (14:20 +0200)]
BUG/MINOR: mux-h1: Discard pending raw data after a tunnel rejection

When a backend rejects a CONNECT request, some early tunnel data may
still be pending in the request buffer. Once the response is complete,
output processing is unblocked with both messages in DONE state. Trying
to process the pending data then reports a processing error, although
these data may legitimately have been sent before the client learned
about the rejection.

Discard RAW_DATA blocks in this situation instead of reporting an error.
Only the backend path with both messages in DONE state does this, and
other block types are still rejected. No discarded bytes are written to
the server connection, so they cannot contaminate a subsequent request.

The discard operation respects the requested byte count. Also exempt
pending raw request data from the final DONE-state error check: a partial
discard, output congestion or allocation failure may leave such data to
be processed on a later call. Without this exception, consuming only part
of a raw block would still report the same processing error.

Successful tunnels and the response path keep their existing behavior.
This implementation relies on RAW_DATA blocks and is only intended for
3.5; it must not be applied as-is to older branches.

6 weeks agoBUG/MEDIUM: mux-h1: Don't subscribe for sends while output is blocked
Christopher Faulet [Tue, 29 Sep 2026 12:20:26 +0000 (14:20 +0200)]
BUG/MEDIUM: mux-h1: Don't subscribe for sends while output is blocked

A wakup loop could be experienced when tunnel data remained blocked on
server side while waiting for the server response.

Indeed, after sending a CONNECT request, the backend H1 mux blocks output
processing until the server response decides whether a tunnel is
established. Early tunnel data must remain pending during this time.

When the stream connector subscribes for sends, h1_subscribe() normally
subscribes to the transport too if the mux has no pending send
subscription. However, when output processing is blocked, the socket may
already be writable. The transport then wakes the mux, which wakes the
stream, which cannot send and subscribes again. This causes a wakeup loop
while waiting for the response.

Do not add a transport send subscription while H1S_F_TX_BLK is set. Keep the
stream's subscription so that the receive path can wake it when the tunnel
is established, the rejection response completes, or EOS is reported. This
does not prevent flushing the mux's output buffer: h1_send() maintains its
own transport subscription for pending output.

This should be backported to stable versions after a period of observation.

6 weeks agoBUG/MAJOR: mux-h2: Preserve raw data on aborted frontend tunnels
Christopher Faulet [Tue, 29 Sep 2026 12:20:26 +0000 (14:20 +0200)]
BUG/MAJOR: mux-h2: Preserve raw data on aborted frontend tunnels

Since the commit 523112b72 ("MEDIUM: mux-h2: Use RAW_DATA blocks to store
tunneled data"), raw data received after a tunnel abort on client side were
not properly handled. These data were added as plain HTTP data, triggering a
BUG_ON() because it is invalid by definition. Instead they must be handled
as true raw data from the HTX point of view.

So now, we properly use RAW_DATA for aborted tunnels on the frontend. The
backend must continue to use regular DATA blocks for the rejection response,
whose payload and trailers remain valid. This matches the existing handling
of trailers and END_STREAM on aborted tunnels.

A regression test was added with early tunnel data left pending, then more
data received after the rejection headers while the response is still
pending.  It reproduces the HTX assertion without this fix.

It is a 3.5-specific issue. No backport is needed.

6 weeks agoMINOR: htx: Remove htx_move_blk_before() function
Christopher Faulet [Tue, 29 Sep 2026 07:11:20 +0000 (09:11 +0200)]
MINOR: htx: Remove htx_move_blk_before() function

This function is used and ambiguous. Indeed, it is not clear how to used
it. The caller is responsible to know what he does. But at the end, it is a
not so easy. Especially about the way the EOM flag is handled.

So to avoid bugs, it is safer to remove the function.

6 weeks agoBUG/MINOR: chunk: Allow large chunks uses from large const buffers
Christopher Faulet [Tue, 29 Sep 2026 06:54:25 +0000 (08:54 +0200)]
BUG/MINOR: chunk: Allow large chunks uses from large const buffers

When a chunk was retrieved via get_best_trash_chunk() or
alloc_best_trash_chunk(), it was not possible to get a large chunk when the
original buffer was a constant 0-sized buffer, independently on size of data
pointed by it.

With this patch, we relaxe the test. It is now possible to get a large
chunks from a 0-sized buffer if it points on large data. Indeed, in that
case, it means that data are hosted in a large buffer, so it is safe to get
a large chunk if possible.

This patch should be backported to 3.4 after a period of observation.

6 weeks agoBUILD: haring: fix compile issue due to nop warning.
Emeric Brun [Mon, 28 Sep 2026 13:22:50 +0000 (15:22 +0200)]
BUILD: haring: fix compile issue due to nop warning.

Compile issue introduced by commit c0b39ea75e ("DEV: haring: bound the
ring geometry to the size of the file").

This patch introduce a warning at compile the binary is not
compiling in case of -Wno-error because of statement with no effects.

In addition it seems also an error since the added static var was never set.

It seems to use a static to rise an error message only one time. So I
suppose it should be set to 1 as soon the error message is printed.

This patch must backported until 3.4.

6 weeks agoRevert "CLEANUP: proxy: mention that px->conn_retries isn't relevant in some cases"
Aurelien DARRAGON [Mon, 28 Sep 2026 13:39:58 +0000 (15:39 +0200)]
Revert "CLEANUP: proxy: mention that px->conn_retries isn't relevant in some cases"

This reverts commit bd48e26a747016acfddce2929443f84fdb9e6997.
Indeed, since commit b71d27e253 ("BUG/MINOR: stream: Fix custom max-retries
initialization when setting backend"), the proxy conn_retries parameter is
now taken again into account for proxies lacking the CAP_FE capability.
Let's remove comments that says otherwise.

6 weeks agoREGTESTS: log: check that a ring's internal server may target a UNIX socket
Yeonggi Kim [Tue, 22 Sep 2026 12:10:13 +0000 (21:10 +0900)]
REGTESTS: log: check that a ring's internal server may target a UNIX socket

This test covers the bug fixed by this commit:

    ce6071c3b BUG/MEDIUM: server: skip log backend addr checks for internal proxies

It declares a ring whose server targets a UNIX socket bound by a
log-forward section, itself relaying to a vtest syslog receiver. It
fails to parse on an affected, unpatched build ("log server address
family not supported for log backend server").

The test pins "chroot /" in its global section: when haproxy runs as
root without an explicit "chroot" directive, its automatic "chroot
auto" feature (641fe4f11) isolates the process into an empty directory
right after startup. That breaks the ring's own UNIX-socket "server"
connect, a runtime, repeatedly-retried client connection, unlike the
log-forward listener's "bind unix@..." which is already open before
the chroot happens. Pinning "chroot /" makes the test behave the same
whether run as root or not.

This must be backported wherever the fix above is backported.

6 weeks agoBUG/MEDIUM: server: skip log backend addr checks for internal proxies
Yeonggi Kim [Tue, 22 Sep 2026 12:10:12 +0000 (21:10 +0900)]
BUG/MEDIUM: server: skip log backend addr checks for internal proxies

23e5f18b8 ("MEDIUM: sink: change the sink mode type to PR_MODE_SYSLOG")
tagged a sink's forward_px (the internal proxy backing a "ring" section)
with PR_MODE_SYSLOG "for compat checks" purposes, stating no behavior
change was expected. This unintentionally made ring's internal "server"
line subject to _srv_check_proxy_mode()'s log-backend compatibility
checks, which were only meant for real "mode log" backends.

Internal proxies (PR_CAP_INT) now fall through to the regular "else"
branch, i.e. the generic STREAM-transport check instead of the
family/DGRAM checks meant for log backends. Two independent
consequences follow from this, both regressions introduced by
23e5f18b8:

1. A "ring" section whose server targets a UNIX socket (a common
   pattern to feed a local "log-forward" section) is rejected at parse
   time with "log server address family not supported for log backend
   server", even though this worked fine before 23e5f18b8.

   Reproducer:

       global

       ring buf1
           server srv1 /tmp/repro-log-forward.sock

       log-forward relay
           bind unix@/tmp/repro-log-forward.sock mode 660
           log ring@buf1 local0

2. Less visibly, a ring server given a DGRAM address (e.g.
   "server s1 udp@127.0.0.1:1514") was, until now, let through the
   log-backend family check (DGRAM is not rejected there) and reaches
   connect_server() at runtime, which promptly segfaults in
   conn_select_mux_be() -> proto_is_quic() on a NULL proto, since a
   ring's forward_px only ever expects a STREAM transport. This patch
   also closes that crash, by routing rings back through the regular
   STREAM-only check that non-log backends already get.

40eb1dd13 ("BUG/MEDIUM: sink: fix unexpected double postinit of sink
backend") already gives the sink's forward_px the PR_CAP_INT capability
for a related reason (skip the automatic log-backend postinit, since
sink manages it manually). This reuses that same flag to also skip the
log-backend-specific address checks for the same class of proxies,
since they are never real user-configured log backends.

This should be backported up to 3.3, but only where 23e5f18b8 and
40eb1dd13 are backported too - without the latter (which sets
PR_CAP_INT on forward_px), this patch is a silent no-op.

6 weeks agoBUG/MEDIUM: sink: reconnect attempt not working after session lasted more than ~25...
Aurelien DARRAGON [Mon, 28 Sep 2026 09:08:43 +0000 (11:08 +0200)]
BUG/MEDIUM: sink: reconnect attempt not working after session lasted more than ~25 days

As described in GH #3508, if a sink session lasted for more that 24.85
days and suddenly stops, the automatic sink session restart mechanism
won't try to reconnect to the server as it would normally. This regression
was introduced by commit 9561b9f ("BUG/MINOR: sink: add tempo between 2
connection attempts for sft servers"). The underlying cause is that we
rely on a 32 timer which is updated at the time of the last connection
attempt, which will eventually wrap when no connection attempt is
performed for more than 24.85 days (this is the case when the session
uptime reaches that time). In that case the timer ends up being in the
future and will not be considered as expired anymore when compared with
now_ms.

To fix the issue, we now look at the applet release/closing date instead
of its creation, this way we ensure that sft->last_close should not wrap
because the time between the applet release and a new attempt is relatively
short, since any new message queued in the forwarding queue will cause the
applet to perform a new attempt. Thus it is not expected that the timer
holds value greater than a few seconds, minutes or worst case hours.

However we need to mention a special case: if the syslog server becomes
unavailable for more that ~25 days, in that case stf->last_close could
wrap, causing the applet to stop trying to connect to the server, but
this is acceptable because a server that is not available for that long
can be considered as dead.

Thanks to GH user @ygkat for reporting the issue and providing very precise
analysis of the bug with a patch proposal, although the final patch followed
Willy's suggestion.

It should be backported to all stable versions (anywhere 9561b9f was
backported).

6 weeks agoBUG/MAJOR: mux_quic: fix potential crash on RESET_STREAM receive
Amaury Denoyelle [Fri, 25 Sep 2026 14:03:07 +0000 (16:03 +0200)]
BUG/MAJOR: mux_quic: fix potential crash on RESET_STREAM receive

When a RESET_STREAM frame is received and upper layer is not allocated,
QCS instance is immediately locally closed and inserted into QCC purge
list. This operation has been performed since the following patch.
Before, the QCS would remain allocated and could possibly prevent the
connection closure if accounted as a request stream.

  commit b7e0aa231aa7af214c97f9540f7bba8498898b11
  BUG/MAJOR: mux_quic: fix leak on RESET_STREAM reception

Sadly, this causes a crash if QCS is already inserted in QCC send list.
This is because <el_send> element is used both for send and purge list,
as a QCS cannot be present in both at the same time.

Fix this skipping insertion in purge list if QCS is already present in
the send list. QCS is not closed locally either in this case. This
allows the QCS to finalize its emission before being closed and purged.

This latter case requires to extend check in qcm_ctrl_send() for QCS
purging when everything is emitted. This is now also performed if a
RESET_STREAM has been received and upper stream layer is not present.

These modifications reveal that it is probably not valid to silently
close a QCS without notifying the peer. Reaction to a RESET_STREAM is in
fact dependent on the application layer protocol and the QUIC stream
type. For now, the current code base should ensure that a bidi HTTP/3
stream is not kept forever after peer reset. A more thoughtful approach
may be implemented later in a future patch.

This must be backported up to 2.8.

6 weeks agoMINOR: log: add the +utf8 encoding option to let valid UTF-8 pass
Willy Tarreau [Mon, 28 Sep 2026 03:21:40 +0000 (05:21 +0200)]
MINOR: log: add the +utf8 encoding option to let valid UTF-8 pass

Since commit db9492bcf ("BUG/MEDIUM: log: encode the whole non-printable
range in the +json output"), the +json encoder escapes every byte outside
the 32..126 range. This guarantees a pure ASCII output that cannot corrupt
a terminal, and it also purposely affects valid UTF-8 text: U+00E9,
encoded as 0xC3 0xA9, is logged as "\u00C3\u00A9", which a JSON decoder
turns into U+00C3 U+00A9. Some users relying on +json to log non-ASCII
text now get unreadable records.

User @Wahnes reported in GH issue #3512 that it breaks their use case
where UTF-8 was expected to land as-is in the output, as permitted by
RFC 8259. However, the patch above purposely avoided this because a
number of valid UTF-8 sequences can have effects on the terminal, such
as direction overrides (RTL/LTR), zero-width characters and a few others
that could affect how the text is rendered, and be used to hide actvities
if retrieved verbatim from header field values for example.

This patch adds a new "+utf8" log option that explicitly lets valid
UTF-8 pass unencoded in JSON, and only that, so that non-utf-8 bytes
above 126 remain encoded. The C1 class of control chars (0x80..0x9f)
which may appear encoded in UTF-8 (e.g. 0xc2 0x9b for 0x9b or \u009b)
remain encoded to protect terminals. This flag is stored in a new flag
LOG_OPT_UTF8 that is only used by LOG_OPT_ENCODE_JSON for now.

The doc and the regtest were updated accordingly.

This may be backported wherever db9492bcf is backported, along with the
previous commit, so that users of versions where it gets backported can
restore the ability to log UTF-8 text.

6 weeks agoMINOR: log: pass the input end to the _lf_encode_bytes() byte encoders
Willy Tarreau [Mon, 28 Sep 2026 03:19:48 +0000 (05:19 +0200)]
MINOR: log: pass the input end to the _lf_encode_bytes() byte encoders

The byte encoding callbacks used by _lf_encode_bytes() only receive the
current byte, so they cannot look at the following ones. This will be
needed by the json encoder to detect multi-byte UTF-8 sequences. Let's
pass them <bytes_stop> as well as an end pointer, which is NULL for
NUL-terminated inputs.

6 weeks agoBUG/MINOR: ssl: free curves, sigalgs and client_sigalgs in srv_parse_*()
William Lallemand [Sun, 27 Sep 2026 19:02:22 +0000 (19:02 +0000)]
BUG/MINOR: ssl: free curves, sigalgs and client_sigalgs in srv_parse_*()

When curves, sigalgs, or client-sigalgs are parsed on a server line,
the string allocated via strdup() was assigned directly to
newsrv->ssl_ctx without releasing any prior value.

If the server inherited these SSL settings from default-server via
srv_ssl_settings_cpy(), or if the keyword was specified multiple
times on the same server line, the previous string was overwritten
and leaked.

Free the previous pointers using ha_free() before assigning the new
strdup() value in srv_parse_curves(), srv_parse_sigalgs(), and
srv_parse_client_sigalgs().

This change should be backported to all supported branches.

6 weeks agoBUG/MINOR: ssl: free curves, sigalgs and client_sigalgs in ssl_sock_free_srv_ctx()
William Lallemand [Sun, 27 Sep 2026 15:18:08 +0000 (15:18 +0000)]
BUG/MINOR: ssl: free curves, sigalgs and client_sigalgs in ssl_sock_free_srv_ctx()

The ssl-default-server-* keywords (curves, sigalgs, client_sigalgs)
allocate memory via strdup() that was never freed when servers are
destroyed. This causes a memory leak per server instance.

Add ha_free() calls guarded by the same OpenSSL API version checks
matching the allocation guards in cfgparse-ssl.c.

This change should be backported to all supported branches.

6 weeks agoBUG/MINOR: ssl: copy curves, sigalgs and client_sigalgs in srv_ssl_settings_cpy()
William Lallemand [Sun, 27 Sep 2026 15:17:58 +0000 (15:17 +0000)]
BUG/MINOR: ssl: copy curves, sigalgs and client_sigalgs in srv_ssl_settings_cpy()

When a server inherits SSL settings from default-server, the curves,
sigalgs and client_sigalgs fields were not copied despite being
allocated by ssl-default-server-* keywords. This causes use-after-free
when the original ssl_ctx is destroyed while another reference still
holds the pointer.

Add strdup() calls guarded by the same OpenSSL API version checks
used elsewhere in the copy function.

Fixes issue #3506.

This change is should be backported to all supported branches.

6 weeks agoBUG/MINOR: acme: exact domain match in acme_challenge_ready()
Jérôme Billiras [Sat, 26 Sep 2026 19:25:39 +0000 (21:25 +0200)]
BUG/MINOR: acme: exact domain match in acme_challenge_ready()

acme_challenge_ready() compares the domain passed on the CLI (or via the
Lua ACME.challenge_ready() function) with strncmp() limited to the
length of the authorization's domain. This is a prefix match: with a
certificate covering "example.com" and "example.com.au", signaling
"example.com.au" also marks the "example.com" challenge as ready.

Use isteq() so that only the exact domain matches.

This should be backported to 3.3. The code was moved into
acme_challenge_ready() in 3.5, so the fix has to be applied to
cli_acme_chall_ready_parse() in older versions.

6 weeks agoMINOR: init: also set the worker-id in file-less mode
Willy Tarreau [Fri, 25 Sep 2026 08:17:21 +0000 (10:17 +0200)]
MINOR: init: also set the worker-id in file-less mode

The worker-id generation at boot, as done in commit 3639aac31f ("MINOR:
config: add a "worker-id" global keyword to identify the worker"), would
escape the file-less mode that is used with haterm and haload. There's
nothing dramatic in this, but it may end up being surprising with certain
corner-case configs that would want to exploit it (logging, header
contents etc), so better set it now for consistency between any worker
mode.

It was visible by running haterm:

    $ ./haterm -L :8000 -G "stats socket /tmp/sock1 level admin"

and issuing:

    $ socat - /tmp/sock1 <<< "show info"

which didn't show the Worker_id line before the fix. No backport is
needed.

6 weeks agoMINOR: ssl: cleanse TLS ticket keys before freeing
William Lallemand [Fri, 25 Sep 2026 08:01:03 +0000 (08:01 +0000)]
MINOR: ssl: cleanse TLS ticket keys before freeing

Cleanse tlskeys with ha_memset_s() before freeing.

tlskeys contains tls_sess_key structures holding encryption keys for
session resumption. Leaving these secrets in freed heap memory risks
exposure through core dumps or memory disclosure bugs.

This patch cleanses intermediate buffers used to parse the tlskeys as
well.

This fixes issue #3504.

6 weeks agoBUG/MINOR: h2: reject :protocol pseudo-header in H2 responses
Turhan ACAR [Thu, 24 Sep 2026 14:38:35 +0000 (15:38 +0100)]
BUG/MINOR: h2: reject :protocol pseudo-header in H2 responses

RFC 7540#8.1.2.1 mandates that pseudo-headers defined for requests must not
appear in responses. When converting an HTTP/2 response to HTX, h2.c already
rejects a response carrying :authority, :method, :path or :scheme, but the
mask used for that check omits H2_PHDR_FND_PROT. As a result a backend HTTP/2
response that contains the request-only ":protocol" pseudo-header (defined by
RFC 8441 for Extended CONNECT) is accepted and forwarded to the client
instead of being rejected.

Add H2_PHDR_FND_PROT to the response-side rejection mask so that ":protocol"
is handled like the other request pseudo-headers and such a response yields a
502 instead of being forwarded.

There is no known security impact: the offending pseudo-header does not
appear in the emitted HTTP/1 response (which is byte-identical to that of
a valid one); the only effect is that a malformed response is tolerated
instead of being rejected.

This should be backported as far back as 2.4, where the ":protocol"
pseudo-header (H2_PHDR_FND_PROT) was introduced; the maintainer may narrow the
range to the currently maintained branches.

Signed-off-by: Turhan ACAR <ttrnn47@gmail.com>
6 weeks agoBUG/MEDIUM: mux_quic: activate timeout on FE init
Amaury Denoyelle [Wed, 23 Sep 2026 14:45:32 +0000 (16:45 +0200)]
BUG/MEDIUM: mux_quic: activate timeout on FE init

Prior to this patch, QUIC MUX timeout was only queued after receiving
the first bytes of data. Thus the timeout would be inactive after
initialization if nothing is received, with a risk of connection leak.

To properly address this, QUIC MUX task timeout is now queued
immediately after initialization has been performed.

The risk of connection leak caused by QUIC MUX is now greatly reduced.
For example, the previous issue on RESET_STREAM reception handling
cannot cause anymore such leak with the current code.

This must be backported to all stable releases.

6 weeks agoBUG/MAJOR: mux_quic: fix leak on RESET_STREAM reception
Amaury Denoyelle [Wed, 23 Sep 2026 12:32:30 +0000 (14:32 +0200)]
BUG/MAJOR: mux_quic: fix leak on RESET_STREAM reception

On QUIC MUX, <nb_hreq> counter serves to account for the currently
active requests in progress. While this number is non null, the
connection is considered active and cannot be released. The counter is
decremented only when the QCS instance is finally freed. This guarantees
that no truncated request could happen.

An issue happened on RESET_STREAM frame reception as the first frame for
a new QCS instance. In this case, <nb_hreq> is incremented. No progress
will occur as the upper stream layer is not instantiated, and QCS
instance will remains half-closed, which prevents its liberation.

The first issue is that it causes a leak of QCS during the connection
lifetime. Worst, this may prevent the whole connection closure even if
idle timeout as fired : this is because qcc_is_dead() checks <nb_hreq>
first before regarding transport layer status. Note that this only
happens if QUIC MUX timeout is not yet activated, which is the case if
qcc_io_cb() has not yet been executed.

This patch fixes this situation by improving RESET_STREAM receive
handler. Now, an extra check is performed to detect if the frame has
been received before the upper stream layer init. If true, QCS instance
is closed locally and immediately scheduled for purge, which ensures
that <nb_hreq> counter will be decremented as expected.

This must be backported up to 2.8.

6 weeks agoBUG/MEDIUM: h3: Set FIN when last DATA block is sent via zero-copy
Christopher Faulet [Wed, 23 Sep 2026 18:34:31 +0000 (20:34 +0200)]
BUG/MEDIUM: h3: Set FIN when last DATA block is sent via zero-copy

Since commit ab7bb500a ("MEDIUM: h3: Use flags of HTX blocks to detect end
of message during sending"), the end of the message is detected via the
HTX_BLK_FL_EOM flag of consumed HTX blocks. But the zero-copy path in
h3_resp_data_send() jumps to the end without testing this flag. So when the
last DATA block of a message is transferred via zero-copy, "fin" is never
set and no FIN is emitted on the QUIC stream, leaving the peer waiting
indefinitely for the end of the stream.

Fix this by capturing the block's EOM flag before the buffer area swap and
setting "fin" accordingly.

It is a 3.5-specific issue. No backport needed.

7 weeks agoREG-TESTS: http-messaging: Send one request per h2 client in h2_trailers.vtc
Christopher Faulet [Wed, 23 Sep 2026 13:21:09 +0000 (15:21 +0200)]
REG-TESTS: http-messaging: Send one request per h2 client in h2_trailers.vtc

The script fails randomly depending on the load on the machine. C3 and C4 H2
clients are sending sending several invalid requests. However, instead of
received the expected RST_STREAM for some requests, a WINDOW_UPDATE is
received, making the script failed.

So, to avoid this issue, the clients are splitted to send only one h2
request per connection. It seems better this way.

7 weeks agoBUG/MINOR: http-ana: restore the transaction status after early hints
Christopher Faulet [Wed, 23 Sep 2026 05:09:08 +0000 (05:09 +0000)]
BUG/MINOR: http-ana: restore the transaction status after early hints

While a 103 response is being built by consecutive early-hint rules, the
transaction status is set to 103 so that the next rules append their headers
to the same response. Since commit 4c3d3d2a6 ("BUG/MINOR: http-act: Properly
generate 103 responses when several rules are used"), once the response was
finished, or on error, the status was reset to 0.

But the initial status of a transaction is -1, and some code relies on it to
know whether a status was set. This is the case of the deny handling in
http_req_get_intercept_rule(), which only uses the default 403 status if the
status is still -1. So a Lua action returning act.DENY after an early-hint
rule makes haproxy answered with a 500 instead of a 403, and logged a null
status.

Let's restore -1 instead in http_early_hint_end() and on the error paths of
the early-hint action and of the cache early hints. A Lua reg-test is added.

This must be backported to all supported versions. The cache part only
exists in 3.5.

7 weeks agoCLEANUP: mux-h2: replace a non-UTF-8 character in a comment
Christopher Faulet [Wed, 23 Sep 2026 04:40:12 +0000 (04:40 +0000)]
CLEANUP: mux-h2: replace a non-UTF-8 character in a comment

A comment in h2c_dec_hdrs() contains a section sign encoded in ISO-8859-1,
which is the only non-UTF-8 byte in the sources. Some tools consider the
whole file as binary because of it, e.g. "ugrep -I" silently skips it.
Let's spell it "section" instead.

7 weeks agoBUG/MINOR: tcpcheck: refresh the start-line after updating the authority
Christopher Faulet [Wed, 23 Sep 2026 04:40:03 +0000 (04:40 +0000)]
BUG/MINOR: tcpcheck: refresh the start-line after updating the authority

When an HTTP health-check request was built with an absolute URI, each
"host" header set by "http-check send" updated the authority of the URI with
http_update_authority(). This rewrote the start-line, which could move it
elsewhere in the HTX message. So, in tcpcheck_eval_send(), the start-line
pointer <sl> must be renewed in that case.

Let's retrieve the start-line again after each authority update. A reg-test
was added.

This must be backported to all supported versions.

7 weeks agoBUG/MINOR: mux-h1: only mark C-L and T-E as sent once the header is emitted
Christopher Faulet [Wed, 23 Sep 2026 04:39:07 +0000 (04:39 +0000)]
BUG/MINOR: mux-h1: only mark C-L and T-E as sent once the header is emitted

In h1_make_headers(), the H1S_F_HAVE_CLEN and H1S_F_HAVE_CHNK flags, and for
the content-length the message lengths, were set before the header was
formatted into the output buffer. If h1_format_htx_hdr() failed because the
buffer was full, the function stopped and left the header block in the HTX
message so that it was processed again on the next call, which was the
expected way to resume.

But then the flag was already set and the header was skipped as a
duplicate. h1_make_eoh() did not add it back either, since its check relied
on the same flags. The message would then be sent with a body but without
the header announcing its framing.

This requires the output buffer to be full in the middle of the headers. It
is not possible by construction for now. But this relies on the state of the
output buffer when a message starts.

Let's only set these flags and lengths once the header was emitted.

This may be backported to stable versions.

7 weeks agoBUG/MINOR: http-fetch: fix the space check of http_auth_bearer(<hdr>)
Christopher Faulet [Wed, 23 Sep 2026 04:38:06 +0000 (04:38 +0000)]
BUG/MINOR: http-fetch: fix the space check of http_auth_bearer(<hdr>)

When http_auth_bearer() was called with a custom header name, a test was
performed to know if the value contained a space after the scheme, by
comparing the length of the scheme with the length of the header value.

But istsplit() had already advanced <ctx.value> past the scheme and the
space at this point, so the comparison was made with the length of the token
instead:

  - a token of exactly 6 characters, the length of "Bearer", was reported as
    a mismatch, e.g. "Bearer abcdef";

  - a lone "Bearer" without any space was accepted and returned an empty
    token.

Let's compare with the length of the value before it is split. The reg-test
is extended with these two cases.

This should be backported to all supported versions.

7 weeks agoBUG/MINOR: http-fetch: http_auth_bearer() must not match a missing header
Christopher Faulet [Wed, 23 Sep 2026 04:37:37 +0000 (04:37 +0000)]
BUG/MINOR: http-fetch: http_auth_bearer() must not match a missing header

When http_auth_bearer() was called with a custom header name and this header
was not found, the fetch still succeeded and returned an empty string. So a
condition like "{ http_auth_bearer(x-tok) -m found }" was true for a request
without any such header.

Commit 909578520 ("BUG/MINOR: http-fetch: Fix http_auth_bearer() when custom
header is used") intended to report a mismatch in this case, as its message
says, but only did it for a header with a wrong type.

Let's return 0 when the header is not found. A reg-test is added for the
fetch with and without a custom header.

This should be backported as far as 2.6.

7 weeks agoBUG/MINOR: http-ana: count response body failures only once in http_fail_cnt
Christopher Faulet [Wed, 23 Sep 2026 04:36:13 +0000 (04:36 +0000)]
BUG/MINOR: http-ana: count response body failures only once in http_fail_cnt

In http_response_forward_body(), the return_srv_abort and return_bad_res
labels incremented the http_fail_cnt counters of the tracked stick counters,
then continued to the return_error label, which incremented them again. So a
server aborting in the middle of the body, or an invalid response body, was
counted twice.And since return_cli_abort and return_int_err also end at
return_error, a client aborting a download, or an internal error, was counted
as a server failure, while the counter is documented to only count the
failures caused by the servers.

Let's remove the increment from return_error. A reg-test is added with a
server closing in the middle of the body and a client aborting a download.

This was introduced by f2b02cfd9 ("MAJOR: http-ana: Review error handling
during HTTP payload forwarding") so this must be backported up to 2.8.

7 weeks agoBUG/MEDIUM: tcp-rules: ignore "capture len" rules evaluated from a backend
Christopher Faulet [Wed, 23 Sep 2026 04:34:25 +0000 (04:34 +0000)]
BUG/MEDIUM: tcp-rules: ignore "capture len" rules evaluated from a backend

This is the same issue as the one fixed for "http-request capture" by the
previous commit. A "tcp-request content capture <expr> len <n>" rule is
accepted in a "listen" section, and creates a capture slot numbered in this
section. When the section is also used as the backend of another frontend,
the rule writes into the slot array of the stream, which is the one of the
frontend: NULL if the frontend has no capture, which crashes the process on
the first request, or a slot belonging to the frontend, allocated from one
pool and later released to another.

The rule is now ignored when the proxy evaluating it is not the stream's
frontend. The reg-test added by the previous commit is extended with the
same three cases for the tcp-request rule.

This must be backported to all supported versions.

7 weeks agoBUG/MEDIUM: http-act: ignore "capture len" rules evaluated from a backend
Christopher Faulet [Wed, 23 Sep 2026 04:33:53 +0000 (04:33 +0000)]
BUG/MEDIUM: http-act: ignore "capture len" rules evaluated from a backend

An "http-request capture <expr> len <n>" rule creates a capture slot in the
proxy it is declared in. The parser only refuses it in proxies without the
frontend capability, so it is accepted in a "listen" section. But the
capture slots of a stream are allocated from its frontend (s->req_cap is
sized from fe->nb_req_cap), and released through the frontend's capture list
with the frontend's pools.

So when the listen section is also used as the backend of another frontend,
the rule runs with an index taken from the listen's numbering on the slot
array of the other frontend:

  - if this frontend has no capture, s->req_cap is NULL and the first
    request crashes the process;

  - otherwise the slot aliases one of the frontend's slots, it is allocated
    from the listen's pool and later released to the frontend's pool, which
    corrupts the pools. It may also be beyond the end of the slot array.

The slot cannot exist for such streams, so the rule is now ignored when the
proxy evaluating it is not the stream's frontend. This cannot be detected at
parsing time since a backend may be resolved at run time.  The documentation
is updated and a reg-test is added.

This must be backported to all supported versions.

7 weeks agoBUG/MEDIUM: http-htx: don't build the new authority in a rotating trash chunk
Christopher Faulet [Wed, 23 Sep 2026 04:32:55 +0000 (04:32 +0000)]
BUG/MEDIUM: http-htx: don't build the new authority in a rotating trash chunk

When the host header value is replaced, the authority of the URI, if any, is
updated from the new value. Since commit 632b54ebf ("BUG/MEDIUM: http-htx:
Don't use data from HTX message to update authority"), this new value is the
one passed by the caller.

However, http_update_authority() function was relying on a rotating trash
chunk to build the new start-line. So it was possible to crush still in-use
data, in that case the new host header value. This could happen when the HTX
message had to be be defragmented. Because of this issue it was possible to
produce an invalid message for any request carrying an authority in its URI,
i.e. every H2 or H3 request and the H1 absolute-form ones, as soon as the
client sended headers large enough to require a defrag.

To fix it, http_update_authority() now builds the start-line into a
dedicated chunk obtained with alloc_trash_chunk(). This protects every
caller, whatever the storage of <host>. A reg-test was added which removes a
large header to leave a hole, then makes the host header grow, for several
sizes of a filler header.

This must be backported to all supported versions.

7 weeks agoBUG/MEDIUM: log: reserve the trailing 0 in CBOR int and bool encoders
Christopher Faulet [Wed, 23 Sep 2026 04:31:28 +0000 (04:31 +0000)]
BUG/MEDIUM: log: reserve the trailing 0 in CBOR int and bool encoders

Commit 378386e14 ("BUG/MEDIUM: log: always reserve room for trailing 0 when
using CBOR encoding helpers") made the text encoding helpers reserve one
byte for the terminating NULL byte that sess_build_logline_orig() always
appends, but lf_bool_encode() and lf_int_encode() were missed:
_lf_cbor_encode_byte() and cbor_encode_int64_{bin,hex}() were still called
with "dst + size" as the stop pointer. So the trailing 0 could be written
one byte past the end of the output buffer.

Let's pass "dst + size - 1" to these encoders like the other helpers do.
The log_cbor reg-test was extended to sweep a string across the end of the
chunk, followed by an int or a bool, with pool tags enabled.

The was introduced by c614fd3b9 ("MINOR: log: add +cbor encoding
option"), so this must be backported up to 3.0.

7 weeks agoREG-TESTS: http-messaging: Add a script to test interim responses for H1/H2
Christopher Faulet [Wed, 23 Sep 2026 08:41:00 +0000 (10:41 +0200)]
REG-TESTS: http-messaging: Add a script to test interim responses for H1/H2

http_1xx_responses.vtc script was added to tests handling of 1xx interim
responses in H1 and H2.

7 weeks agoBUG/MINOR: mux-h2: Don't expect more HTX data on 1xx interim responses
Christopher Faulet [Wed, 23 Sep 2026 08:01:59 +0000 (10:01 +0200)]
BUG/MINOR: mux-h2: Don't expect more HTX data on 1xx interim responses

Commit b70921f2c ("BUG/MINOR: mux-h2: Properly handle full or truncated HTX
messages on shut") introduced a regression. When the response start-line had
the HTX_SL_F_XFER_LEN flag set, the H2 stream was flagged with
H2_SF_MORE_HTX_DATA, as if a payload was expected after the response
headers. This was also set for 1xx interim responses. If a final response
with an unknown length was received (no HTX_SL_F_XFER_LEN set), the flag
H2_SF_MORE_HTX_DATA was not removed, leading to an error. The H2 stream was
seen as truncated when a shut was performed and it was reset with a
RST_STREAM(CANCEL).

To fix the issue, H2_SF_MORE_HTX_DATA flag is no longer set for 1XX interim
responses.

This patch must be backported as far as 2.8.

7 weeks agoBUG/MEDIUM: h1-htx: Don't report EOM for H1 interim responses during parsing
Christopher Faulet [Wed, 23 Sep 2026 05:34:13 +0000 (07:34 +0200)]
BUG/MEDIUM: h1-htx: Don't report EOM for H1 interim responses during parsing

Commit b7467ca91 ("MAJOR: htx: Use htx_set_eom() instead of setting
HTX_FL_EOM by hand") introduced a regression. During H1 response parsing,
the end of the message was reported in 1XX interim responses, while it
should not. Because of this issue, a BUG_ON() was triggered on the final
response parsing.

The calls to htx_set_eom() function in h1_postparse_req_hdrs() and
h1_postparse_res_hdrs() was factorized to be always set when the parser
reached H1_MSG_DONE state. This was obviously wrong for 1XX interim
messages.

Thanks to Adam Crawford for reporting this issue.

It is a 3.5-specific issue. No backport needed.

7 weeks agoBUG/MINOR: http-client: Convert server timeout ticks when setting it
Christopher Faulet [Mon, 21 Sep 2026 13:54:10 +0000 (15:54 +0200)]
BUG/MINOR: http-client: Convert server timeout ticks when setting it

When the server timeout of an HTTP client instance is set by the caller,
don't forget to convert it to ticks.

This patch could be backported to all supported versions.

7 weeks agoMEDIUM: ssl: add ocsp-update.timeout global option
Alex Szakaly [Fri, 18 Sep 2026 20:22:54 +0000 (22:22 +0200)]
MEDIUM: ssl: add ocsp-update.timeout global option

The httpclient used to fetch OCSP responses during automatic updates
only has a connect timeout (5s), no server timeout: if a response
starts but is never fully delivered, the request can hang forever.
This task and its httpclient are shared by the whole ocsp-update
queue, so a single stuck request blocks every other certificate
waiting behind it, effectively stalling all further OCSP updates.

Add a global "ocsp-update.timeout <timeout>" option and apply it with
httpclient_set_timeout() before each update request is sent. This
bounds the total time spent on one OCSP fetch, from the connection
attempt to the full response, and lets a stuck or overly slow
responder be retried later following the usual
ocsp-update.mindelay/maxdelay rules instead of blocking the queue
indefinitely.

The value is parsed with parse_time_err() using TIME_UNIT_MS, the same
helper and default unit used by every other timeout keyword in the
codebase (e.g. "timeout connect", "tcp-ut"), so it accepts a bare
number in milliseconds or any value suffixed with an explicit unit.
It defaults to 10s (SSL_OCSP_UPDATE_HTTP_TIMEOUT, now expressed in
milliseconds).

Can be backported to stable branches that already carry the other
ocsp-update.* global options.

Signed-off-by: Alex Szakaly <alex.szakaly@gmail.com>
7 weeks agoBUG/MINOR: ssl: Fix possible null deref on the SSL context in ssl_sock_to_buf()
Christopher Faulet [Mon, 21 Sep 2026 06:25:49 +0000 (08:25 +0200)]
BUG/MINOR: ssl: Fix possible null deref on the SSL context in ssl_sock_to_buf()

In ssl_sock_to_buf(), before exiting, the SSL context was systematically
dereferenced to clear SSL_SOCK_F_KTLS_RX_CTRL flag. However, at the
beginning, the SSL context is tested. So theorically, it seems possible to
enter with a NULL SSL context. So to avoid any NULL dereference, the SSL
context is now tested before clearing SSL_SOCK_F_KTLS_RX_CTRL flag

This patch should solve the issue #3465. It should be backported as far as
3.3.

7 weeks agoBUG/MINOR: hlua: release private Lua rule data on teardown
M9nx [Mon, 24 Aug 2026 04:15:42 +0000 (07:15 +0300)]
BUG/MINOR: hlua: release private Lua rule data on teardown

Lua actions and services allocate a `struct hlua_rule` (and sometimes an
arguments array) while parsing the configuration. The resulting private
structure is stored in the generic `act_rule` structure.

However, these parsers failed to register a `release_ptr` callback. As a
result, when `free_act_rule()` destroys the parent rule during proxy
teardown (e.g. at process exit or soft-reload deinit), it leaks the
Lua-private allocation. This is exposed by LeakSanitizer when validating
configurations that use Lua rules (e.g. `examples/games.cfg -c`).

This is a configuration/process-lifetime data leak rather than a
per-request or per-connection leak. It does not accumulate during normal
runtime operation.

This patch fixes the issue by adding a common release callback and
assigning it in all three Lua parsers that allocate this structure
(`action_register_lua`, `action_register_service_http`, and
`action_register_service_tcp`). The callback frees the `hlua_rule`
and any owned argument strings, while deliberately leaving the borrowed
`hlua_function` untouched.

This fixes issue #3480.

Backport status: may be backported to all supported versions.

7 weeks agoREGTESTS: http-messaging: Add a test for the CONNECT tunnels
Christopher Faulet [Fri, 18 Sep 2026 16:24:45 +0000 (18:24 +0200)]
REGTESTS: http-messaging: Add a test for the CONNECT tunnels

This test checks the CONNECT tunnels handling, on the H1 and H2 sides. For a
tunneled stream, the end of the HTTP message is reported just after the
headers, so the tunneled data are exchanged after the end of the message.
Four cases are covered:

  * A H1 CONNECT with tunneled data exchanged in both directions.

  * A H2 CONNECT with the tunneled data sent in the same segment than the
    headers. This way, they are received by the H2 multiplexer before the
    request was forwarded.

  * A H2 CONNECT followed by a bogus trailers block. It must be rejected
    because the message was already ended. Tunneled data are sent first, so
    the end of the message is reported on the HTX message whether the headers
    part was already consumed or not. A barrier is used to be sure the tunnel
    is established when the trailers are sent.

  * A H2 CONNECT refused by the server with a payload and trailers. Here the
    tunnel attempt is aborted, so the response is a regular one and its
    trailers must be forwarded.

7 weeks agoDOC: internals: Update the HTX API documentation for tunneled data
Christopher Faulet [Tue, 15 Sep 2026 12:00:58 +0000 (14:00 +0200)]
DOC: internals: Update the HTX API documentation for tunneled data

The HTX API documentation was updated to reflect the addition of the RAW_DATA
block type:

  * The new block type was added in the list of supported types and in the
    typical composition of an HTX message, the tunneled data being always
    located after the end of the message.

  * The section about the end of the message was rewritten. Tunneled data are
    no longer stored in a separate buffer, they have their own block type. It
    is now stated that testing the tail block is enough because a RAW_DATA
    block may only be found after the end of the message. It is also stated
    that the block carrying the flag may already have been consumed, an HTX
    message being a partial and stateless representation of an HTTP message.

  * htx_add_raw_data(), htx_add_data_type(), htx_add_data_type_atonce(),
    htx_is_data_type() and htx_msg_ended() were documented, the last one
    replacing htx_has_eom(). It is also stated that htx_add_last_data() is
    dedicated to regular DATA blocks and that htx_defrag() merges consecutive
    blocks of the same payload type.

7 weeks agoMINOR: mux-h1: Only handle RAW_DATA blocks when emitting tunneled data
Christopher Faulet [Fri, 18 Sep 2026 08:11:16 +0000 (10:11 +0200)]
MINOR: mux-h1: Only handle RAW_DATA blocks when emitting tunneled data

As expected, now the changes to properly handle the RAW_DATA blocs were
performed, the h1_make_tunnel() function is updated to only handle RAW_DATA
blocks.

7 weeks agoMINOR: http-htx: Report the payload of RAW_DATA blocks in internal.htx_blk.data
Christopher Faulet [Fri, 18 Sep 2026 11:21:39 +0000 (13:21 +0200)]
MINOR: http-htx: Report the payload of RAW_DATA blocks in internal.htx_blk.data

The "internal.htx_blk.data" sample fetch only reported the payload of DATA
blocks. Because tunneled data are now stored in RAW_DATA blocks, and because
"internal.htx_blk.type" reports this new type, an empty string was returned
for such block while its type was properly displayed.

These sample fetches are debugging helpers. So both types are now handled,
htx_is_data_type() being used to test it.

7 weeks agoMINOR: http-htx: Really test the EOM flag presence in internal.htx.has_eom
Christopher Faulet [Tue, 15 Sep 2026 12:00:58 +0000 (14:00 +0200)]
MINOR: http-htx: Really test the EOM flag presence in internal.htx.has_eom

The "internal.htx.has_eom" sample fetch is documented to return true if the
HTX message contains the end-of-message flag. It relied on htx_msg_ended()
to do so. But this function now also reports the end of the message when the
block carrying the flag was already consumed, which is the case when the
tail block is a RAW_DATA block.

Because this sample fetch is a debugging helper, it must really answer the
question it is documented for. So the HTX message is now scanned, looking for
a block carrying the flag. Note the scan is performed from the head on
purpose. This way, no assumption is made about the position of the block
carrying the flag and an unexpected one would be reported.

The documentation was updated to mention the flag may already have been
consumed, in which case false is returned while the end of the message was
already reached.

7 weeks agoRevert "MEDIUM: mux-h2: Don't mix HTTP and tunneled data in the same buffer"
Christopher Faulet [Tue, 15 Sep 2026 12:00:43 +0000 (14:00 +0200)]
Revert "MEDIUM: mux-h2: Don't mix HTTP and tunneled data in the same buffer"

This reverts commit b6433eb62 (the rename of htx_has_eom() into
htx_msg_ended() was taken into account).

Because the end of the message is carried by the tail block of the HTX
message, HTTP data and tunneled data could not be stored in the same
buffer. Adding tunneled data after the block carrying the EOM flag would
have hidden the end of the message. So the H2 multiplexer had to store them
in a separate buffer and to delay their transfer to the upper layer until
the HTTP part was fully consumed.

Tunneled data are now stored in RAW_DATA blocks. They can no longer be
confused with HTTP data and htx_msg_ended() is able to report the end of the
message when the tail block is such block. So this workaround is no longer
necessary. The original behavior is restored: only one rxbuf is used and the
transfer of the tunneled data is no longer delayed.

7 weeks agoMEDIUM: mux-h2: Use RAW_DATA blocks to store tunneled data
Christopher Faulet [Tue, 15 Sep 2026 12:00:23 +0000 (14:00 +0200)]
MEDIUM: mux-h2: Use RAW_DATA blocks to store tunneled data

For a tunneled H2 stream, the end of the HTTP message is reported just after
the headers, when H2_SF_BODY_TUNNEL flag is set. So any DATA frame received
after that carries tunneled data and must be stored in a RAW_DATA block.
h2_frt_transfer_data() was updated accordingly.

Thanks to this change, HTTP data and tunneled data are no longer mixed on
the H2 side. The early end of the message is thus preserved when tunneled
data are received, and the next commit is able to remove the workaround that
was necessary to keep both in separate buffers.

7 weeks agoMEDIUM: mux-h1: Use RAW_DATA blocks to store tunneled data
Christopher Faulet [Tue, 15 Sep 2026 12:00:08 +0000 (14:00 +0200)]
MEDIUM: mux-h1: Use RAW_DATA blocks to store tunneled data

When a H1 stream is switched in tunnel mode, the transfer length is unknown
and the data are read till the shutdown. This is exactly the same code path
than a message without any transfer length information. So the block type to
use is chosen depending on the parser state: in H1_MSG_TUNNEL state, the
data are tunneled data and a RAW_DATA block is used. Otherwise it is still an
HTTP payload and a DATA block is used.

h1_copy_msg_data() takes the block type as extra argument for that purpose.

Thanks to this change, HTTP data and tunneled data are no longer mixed on
the H1 side. It also means the end of the message, reported just after the
headers for a tunneled stream, is now preserved when tunneled data are
received.

7 weeks agoMINOR: mux-h2: Handle RAW_DATA blocks when sending data
Christopher Faulet [Tue, 15 Sep 2026 12:00:08 +0000 (14:00 +0200)]
MINOR: mux-h2: Handle RAW_DATA blocks when sending data

Tunneled data are now stored in RAW_DATA blocks. The H2 multiplexer must be
able to send them, to be able to forward a tunnel established on the other
side.

For the H2 multiplexer, there is no difference with HTTP data: both are sent
in DATA frames. Only the ability to handle the new type is added here. No
producer emits such block yet.

7 weeks agoMINOR: mux-h1: Handle RAW_DATA blocks when sending data
Christopher Faulet [Tue, 15 Sep 2026 11:59:50 +0000 (13:59 +0200)]
MINOR: mux-h1: Handle RAW_DATA blocks when sending data

Tunneled data are now stored in RAW_DATA blocks. The H1 multiplexer must be
able to send them. h1_make_tunnel() is responsible to do so.

For the H1 multiplexer, there is no difference with HTTP data: both are sent
verbatim. Only the ability to handle the new type is added here. No producer
emits such block yet so h1_make_tunnel() relies on htx_is_data_type() for
now. But once all changes done, this will have to be modified to accept
RAW_DATA blocks only.

7 weeks agoMEDIUM: htx: Allow tunneled data to be added in a message already ended
Christopher Faulet [Tue, 15 Sep 2026 11:59:32 +0000 (13:59 +0200)]
MEDIUM: htx: Allow tunneled data to be added in a message already ended

For a tunneled stream, the end of the HTTP message is reported just after
the headers. It is an "early" EOM: from the HTTP point of view the message
is finished and only tunneled data may be exchanged then. But nothing may be
added in a message already ended, otherwise the end of the message would no
longer be reported on the tail block.

Now tunneled data have their own block type, they can be added in a message
already ended without breaking this rule. htx_add_raw_data() was added for
that purpose. It is the only function able to add a block in such message,
and the corresponding BUG_ON() were relaxed accordingly.

htx_msg_ended() was updated to also report the end of the message when the
tail block is a RAW_DATA block. Indeed, tunneled data may only be found
after the end of the message. So, if there are some, the end of the message
was necessarily seen. Either the block carrying the flag is still in the HTX
message, or it was already consumed. In this last case, it is the consumer
responsibility to remember it.

Finally, the test on the end of the message was removed from htx_xfer()
because blocks are transferred one by one, using the safe functions. So
invalid additions are detected there, with a better accuracy.

7 weeks agoMINOR: htx: Handle RAW_DATA blocks like DATA blocks in the HTX API
Christopher Faulet [Tue, 15 Sep 2026 11:59:06 +0000 (13:59 +0200)]
MINOR: htx: Handle RAW_DATA blocks like DATA blocks in the HTX API

From the HTX API point of view, a RAW_DATA block is exactly the same as a
DATA block. It carries a payload that may be partially consumed. So the
functions dealing with this property must handle both types the same way:

  * htx_truncate() must be able to truncate such block
  * htx_drain() must be able to partially drain it
  * htx_xfer() must be able to partially transfer it and must of course take
    care to preserve its type

There is no change of behavior because no producer emits RAW_DATA blocks
yet.

7 weeks agoMINOR: htx: Add the block type as argument of the data insertion functions
Christopher Faulet [Tue, 15 Sep 2026 11:58:52 +0000 (13:58 +0200)]
MINOR: htx: Add the block type as argument of the data insertion functions

htx_add_data() was renamed into htx_add_data_type() and takes the block type
as extra argument. htx_add_data() is now a thin wrapper on it, passing
HTX_BLK_DATA. The tests on the tail block type were changed accordingly to
be performed against the type of the block to add, instead of hardcoding
HTX_BLK_DATA.

The same was done for the "atonce" version. __htx_add_data_atonce() was
renamed into __htx_add_data_type_atonce() and also takes the block type as
extra argument. A public and guarded version, htx_add_data_type_atonce(), was
added on top of it and htx_add_data_atonce() is now a thin wrapper on this
one, passing HTX_BLK_DATA. Note htx_add_last_data() still relies on the
private and unguarded version because it must be able to insert a block after
the one carrying the end of the message, before moving it at the right
place. It remains dedicated to regular DATA blocks.

Finally, htx_defrag() was updated to pass the block type. This way, RAW_DATA
blocks are merged when a message is defragmented, exactly like DATA blocks.
Otherwise they would be copied one by one, keeping a block per received
chunk.

This way, RAW_DATA blocks will be able to be inserted using the same
functions, and thus the same logic to append data to the tail block or to
deal with the free space in the message.

There is no change of behavior. Only HTTP data can be added for now.

7 weeks agoMINOR: htx: Add HTX_BLK_RAW_DATA block type
Christopher Faulet [Tue, 15 Sep 2026 11:58:34 +0000 (13:58 +0200)]
MINOR: htx: Add HTX_BLK_RAW_DATA block type

An HTX message is a partial and stateless representation of an HTTP
message. Tunneled data are not part of the HTTP message. However, for now,
they are stored in DATA blocks, exactly like the HTTP payload. So there is
no way to make the difference between both.

A dedicated block type, HTX_BLK_RAW_DATA, is added to fix this. For now, no
producer emits such block. Only the HTX API is able to handle it. From this
point of view, a RAW_DATA block is exactly the same as a DATA block. Both
carry a payload that may be partially consumed. htx_is_data_type() function
was added to test this property on a block type.

It is important to add the new type in htx_get_blk_value() and
htx_set_blk_value_len() switches. Both have a default case silently doing
nothing. So a missing entry would lead to an empty payload on one side and
to a size never updated on the other side.

7 weeks agoMINOR: htx: Rename htx_has_eom() into htx_msg_ended()
Christopher Faulet [Tue, 15 Sep 2026 11:58:16 +0000 (13:58 +0200)]
MINOR: htx: Rename htx_has_eom() into htx_msg_ended()

htx_has_eom() name suggests the HTX message contains a block carrying the
HTX_BLK_FL_EOM flag. It is true for now, because the flag can only be found
on the tail block. But this will change. The block carrying this flag may be
consumed while the end of the message must still be reported. So the name is
misleading and htx_msg_ended() is more accurate: it states the end of the
message was reached, regardless of the presence of the flag in the message.

Only a rename, there is no change of behavior. Note the sample fetch
"internal.htx.has_eom" was not renamed because it is part of the
configuration.

7 weeks agoDOC: internals: Update the HTX API documentation
Christopher Faulet [Mon, 14 Sep 2026 07:00:20 +0000 (09:00 +0200)]
DOC: internals: Update the HTX API documentation

The HTX API documentation was not up-to-date anymore. It still described the
end of the message as a flag on the HTX message itself and said nothing
about the flags on the HTX blocks. Let's update it to reflect the recent
changes:

  * A block's metadata is now composed of 3 fields. The flags field was
    added and the address field is now a 28 bits field, limiting the
    payloads part to 256 MB.

  * The end of the message is now marked by the HTX_BLK_FL_EOM flag, set on
    the last block of the message. htx_set_eom() and htx_has_eom() must be
    used to set it and to test it. Because the flag is carried by the tail
    block, nothing must be added in a message already ended. Only headers
    and trailers may still be added because they are moved at the right
    place, before the corresponding end-of marker.

  * htx_get_first_blk(), htx_get_prev_blk() and htx_get_next_blk() now skip
    the unused blocks and htx_remove_blk() never leaves an unused block on
    the head or on the tail of the message. So there is no longer any need
    to test the block's type against HTX_BLK_UNUSED when looping on an HTX
    message.

  * htx_is_unique_blk() was removed.

  * The restrictions on htx_xfer() and htx_append_msg() were added and
    htx_skip_msg_payload(), which was not documented at all, was added.

In addition, few pre-existing errors were fixed. htx_move_blk_before() moves
a block before the pivot and not after it. The function used to add a
trailer is htx_add_trailer() and not htx_add_trailers(). And a duplicated
paragraph about htx_remove_blk() was removed.

7 weeks agoMEDIUM: htx: Remove usless htx_is_unique_blk() function
Christopher Faulet [Fri, 11 Sep 2026 13:42:50 +0000 (15:42 +0200)]
MEDIUM: htx: Remove usless htx_is_unique_blk() function

Tagged as MEDIUM while it is only a code cleanup, but this function may
still be used by external code. It is an API change.

This function is no longer used but it is also useless. Indeed, thanks to
recent changes, it is not possible to have unused HTX blocks on head and
tail of the message. So, htx_is_unique_blk() can be replaced by a test on
the number of blocks in the message.

In addition, this function was almost always used to know if the EOM flag on
the message can be processed. Now, the info is on the block itself. So no
reason to still use this function. It should disappear naturally with
modifications to handle all recent changes.

So, let's remove it now.

7 weeks agoMEDIUM: htx: Harden HTX API to avoid invalid uses when EOM was reached
Christopher Faulet [Tue, 8 Sep 2026 14:21:12 +0000 (16:21 +0200)]
MEDIUM: htx: Harden HTX API to avoid invalid uses when EOM was reached

With recent changes, the info about the end of the message is now carried by
a block only. In addition, it is really important to never mix HTTP blocks
with tunneled data because we must be able at any time to test the tail
block to know if the EOM flag is set or not. To avoid any invalid uses, the
API was reinforced. BUG_ONs were added to trigger errors when such uses are
detected.

Note it should not be a huge change because mixing http data and tunneled
data was already not supported.

7 weeks agoMEDIUM: mux-h2: Don't mix HTTP and tunneled data in the same buffer
Christopher Faulet [Fri, 11 Sep 2026 15:43:13 +0000 (17:43 +0200)]
MEDIUM: mux-h2: Don't mix HTTP and tunneled data in the same buffer

For a tunneled stream, the end of the HTTP message is reported just after
the headers while the tunneled data are received after. Because the
information about the end of the message was a flag on the HTX message
itself, both could be stored in the same buffer without any issue.

This is no longer true. The end of the message is now carried by the tail
block of the HTX message. So nothing must be added after this block,
otherwise the message would no longer be seen as ended. It means HTTP data
and tunneled data must never be mixed in the same buffer.

Two changes are required for that:

  * h2s_may_append_to_rxbuf() now refuses to append data to an rxbuf
    carrying a message already ended. h2_frt_transfer_data() thus gets an
    extra rxbuf to store the tunneled data. If no buffer is available, the
    demux is blocked, as usual.

  * h2_rcv_buf() no longer transfers an rxbuf to the upper layer when the
    HTX message in the channel buffer was already ended. SE_FL_RCV_MORE and
    SE_FL_WANT_ROOM are reported instead, so the transfer is retried once
    the HTTP part was consumed.

Note the H1 multiplexer does not have this issue because the demux is
blocked, thanks to H1S_F_RX_BLK flag, until the tunnel is established.

This patch is a prerequisite to be able to harden the HTX API and forbid
any addition in a message already ended.

7 weeks agoMAJOR: htx: Remove HTX_FL_HAS_EOM flag
Christopher Faulet [Tue, 8 Sep 2026 06:15:49 +0000 (08:15 +0200)]
MAJOR: htx: Remove HTX_FL_HAS_EOM flag

This flag can now be removed because it is no longer used. This commit is
pretty light but it is marked as MAJOR because it is an API change. All
external code relying on this flag will have to be updated.

7 weeks agoMAJOR: tree-wide: No longer use HTX_FL_HAS_EOM to detect end of message
Christopher Faulet [Tue, 8 Sep 2026 05:33:18 +0000 (07:33 +0200)]
MAJOR: tree-wide: No longer use HTX_FL_HAS_EOM to detect end of message

HTX_FL_HAS_EOM flag on the HTX message is no longer used to mark or detect
the end of the message. htx_has_eom() function is used to detect it and
htx_set_eom() is used to set it (except inside the HTX API). Thus we now
rely on flags on the tail block to deal with the end of the message.

Most of time, the change is quite light and easy to review. However there
are 3 parts no so obvious:

 * The decompression filter: DECOMP_STATE_EOM_SEEN flag is now set only when
   a block is removed. It is the only way to loose the EOM. This part was
   placed in a dedicated function to not duplicate the code. At the end,
   before exiting and when the decompression is finished, the EOM flag is
   added on the tail block if necessary.

 * The compression filter: As for the decompression filter, the end of
   message is only evaluated when an HTX block is removed. It is the only
   way to loose it. And we try to restore it only on the last DATA block.

 * The lua filter API: The internal function used to remove part of the
   payload was updated to be sure to now loos the EOM on the tail block if
   it is removed.

In the HTX API, HTX_FL_HAS_EOM flag is now longet set.

7 weeks agoMINOR: htx: Add function to know if the tail block carry EOM flag
Christopher Faulet [Mon, 7 Sep 2026 05:58:21 +0000 (07:58 +0200)]
MINOR: htx: Add function to know if the tail block carry EOM flag

htx_has_eom() function can now be used to know if the tail block of a
message carry the HTX_BLK_FL_EOM flag, so to know if the current HTX message
contains the end of the message.

htx_expect_more() function relies on it.

7 weeks agoMINOR: hlua: Use flags of HTX blocks to detect EOM from an HTTP applet
Christopher Faulet [Tue, 21 Jul 2026 09:58:22 +0000 (11:58 +0200)]
MINOR: hlua: Use flags of HTX blocks to detect EOM from an HTTP applet

When we try to get request's data from an HTTP applet, instead of testing
HTX_FL_HAS_EOM flag on the message, we now rely on the flags of HTX blocks.
The logic remains the same, when all DATA blocks were consumed, we consider
the request as fully received. It may be when the blocks with the EOM flag
was removed or because a trailer was seen.

7 weeks agoMINOR: stats-html: Detect a complete request by testing flags on the tail bloc
Christopher Faulet [Thu, 9 Jul 2026 14:17:46 +0000 (16:17 +0200)]
MINOR: stats-html: Detect a complete request by testing flags on the tail bloc

In the HTTP stats applet, when a POST is received, instead of testing the
HTX_FL_HAS_EOM flag on the message itself, we are now relying on the tail
bloc to know if the end of the request was received or not, by testing its
flags.

7 weeks agoMEDIUM: h3: Use flags of HTX blocks to detect end of message during sending
Christopher Faulet [Wed, 2 Sep 2026 14:04:26 +0000 (16:04 +0200)]
MEDIUM: h3: Use flags of HTX blocks to detect end of message during sending

When a message is sent, the end of the message is now detected by testing
flags of each HTX blocs. To do the API was slightly changed. The "fin" flag
is not longer set in the main loop in h3_snd_buf() function. Instead, the
flag is set in functions consumming the HTX blocks (h3_req_headers_send,
h3_resp_headers_send...).

H3_SF_SENT_INTERIM flag is no longer tested. An interim message cannot have
EOM set. A BUG_ON() may possibly be added. In addition, the HTX message is
not check for emptiness. Here again a BUG_ON() may be added.

7 weeks agoMINOR: mux-fcgi: Use flags of HTX blocks to detect end of message during sending
Christopher Faulet [Thu, 9 Jul 2026 13:06:41 +0000 (15:06 +0200)]
MINOR: mux-fcgi: Use flags of HTX blocks to detect end of message during sending

When a message is sent, the end of the message is now detected by testing
flags of each HTX blocs. Otherwise, the logic remains mostly unchanged. The
way the end of the message is handled in fcgi_strm_send_stdin() was modified
a bit but the result is the same. Now, we rely on a variable to decide, at
the end, if an empty STDIN record should be sent or not. This avoid an extra
test on the HTX message to be sure it is empty.

7 weeks agoMINOR: mux-h1: Use flags of HTX blocks to detect end of message during sending
Christopher Faulet [Thu, 9 Jul 2026 12:52:58 +0000 (14:52 +0200)]
MINOR: mux-h1: Use flags of HTX blocks to detect end of message during sending

When a message is sent, the end of the message is now detected by testing
flags of each HTX blocs. Otherwise, the logic remains mostly unchanged. A
test in h1_make_eoh() was slightly changed but it should be harmless.
Especially because HTX_BLK_FL_EOM cannot be set on the EOH block if it is
followed by something else than tunneled data, which is unexpected and
invalid in that case. All conditions to establish a tunnel at this stage
were already evaluated. And if any, the error will be detected in
h1_make_trailers() function.

7 weeks agoMINOR: mux-h2: Use flags of HTX blocks to detect end of message during sending
Christopher Faulet [Thu, 9 Jul 2026 09:40:39 +0000 (11:40 +0200)]
MINOR: mux-h2: Use flags of HTX blocks to detect end of message during sending

When a message is sent, the end of the message is now detected by testing
flags of each HTX blocs. Otherwise, most of the logic remains unchanged. The
only difference is the detection of the end of the message when headers are
sent (back or front). Now, it is be possible to detect the EOM when the
stream is already closed and H2_SF_MORE_HTX_DATA may be removed while it was
untouched before. But it is not a big deal because this flag is only tested
when a shutdown is performed on the stream. So removing it for an already
closed stream is a no-op.

7 weeks agoMINOR: fcgi-app: Stop on last HTX block when getting the payload size
Christopher Faulet [Wed, 8 Jul 2026 15:12:27 +0000 (17:12 +0200)]
MINOR: fcgi-app: Stop on last HTX block when getting the payload size

In the loop counting all DATA block to be able to add a "Content-length:"
header in the FCGI response, we now check flags on the HTX block to know if
it is the last one of the message or not. If HTX_BLK_FL_EOM flag is set, it
means we can stop.

7 weeks agoMINOR: compression: Rely on HTX block flags to detect the end of message
Christopher Faulet [Wed, 8 Jul 2026 15:06:04 +0000 (17:06 +0200)]
MINOR: compression: Rely on HTX block flags to detect the end of message

Thanks to previous patches, instead of testing HTX_FL_HAS_EOM flag to detect
the end of the message, we now rely on the flags on the HTX block.

7 weeks agoMEDIUM: htx: Add HTX_BLK_FL_EOM flag and set it on last block of the message
Christopher Faulet [Wed, 8 Jul 2026 14:46:12 +0000 (16:46 +0200)]
MEDIUM: htx: Add HTX_BLK_FL_EOM flag and set it on last block of the message

HTX_BLK_FL_EOM flag was introduced to be set on the last block of an HTX
message. It must be set on the tail block when the HTX_FL_HAS_EOM is set.
Now, only the HTX API must set these flags by hand. From outside of the HTX
API, htx_set_eom() function must be used.

A BUG_ON() was added to unsure the flag is added on valid blocks. It is possible
to add it on EOH, EOT and DATA blocks only.

7 weeks agoMAJOR: htx: Rename HTX_FL_EOM into HTX_FL_HAS_EOM
Christopher Faulet [Thu, 23 Jul 2026 13:03:22 +0000 (15:03 +0200)]
MAJOR: htx: Rename HTX_FL_EOM into HTX_FL_HAS_EOM

It is just a flag renaming but it is an API breakage and this could generate
compilation errors for all external modules relying on the HTX API. This
change was performed to force to review all code using HTX to be sure this
flag is never set by hand from outside. Indeed, htx_set_eom() function must
always be used now.

7 weeks agoMAJOR: htx: Use htx_set_eom() instead of setting HTX_FL_EOM by hand
Christopher Faulet [Wed, 8 Jul 2026 14:13:24 +0000 (16:13 +0200)]
MAJOR: htx: Use htx_set_eom() instead of setting HTX_FL_EOM by hand

The function htx_set_eom() takes care to the HTX_FL_EOM on the HTX message,
by first adding a EOT block if the message is empty. So let's use it
wherever possible.

This commit is tagged as MAJOR because it is an API change.

7 weeks agoMINOR: htx: Add support for flags on HTX blocks
Christopher Faulet [Wed, 8 Jul 2026 13:23:11 +0000 (15:23 +0200)]
MINOR: htx: Add support for flags on HTX blocks

In the commit 32b51cdf3 ("BUG/MINOR: config: reject configs using HTTP with
bufsize >= 256 MB"), the maximum possible size for buffers is limited to 256
MB. So, on HTX side, it means a bloc cannot have a relative address higher
than this value. So on the 32-bits integer used to store the relative block
address, only 28 bits can be used, and 4 bits remains unused.

So these 4 bits were reused to store flags on block themselves. In this
patch, the support for these flags is introduced and the HTX API was adapted
to deal with it.

7 weeks agoMEDIUM: htx: Insert headers and trailers before corresponding end-of block
Christopher Faulet [Wed, 8 Jul 2026 13:15:53 +0000 (15:15 +0200)]
MEDIUM: htx: Insert headers and trailers before corresponding end-of block

htx_add_header() and htx_add_trailer() were updated to automatically insert
the block at the right place, just before the corresponding end-of block.
Indeed, it is invalid, unexepected and probably unsupported to insert a
header after the EOH block without moving it. So it is easier to handle it
in HTX function directly.

It means that http_add_header() was simplified.

7 weeks agoMEDIUM: htx/tree-wide: Remove tests on HTX_BLK_UNUSED when possible
Christopher Faulet [Thu, 23 Jul 2026 09:05:53 +0000 (11:05 +0200)]
MEDIUM: htx/tree-wide: Remove tests on HTX_BLK_UNUSED when possible

Thanks to previous changes, it is possible to remove all tests on
HTX_BLK_UNUSED outside of the HTX API. Indeed, because htx_get_*_blk() and
htx_remove_blk() functions are now skipping automatically unused blocks, we
are sure to never get such blocks from outside.

7 weeks agoMEDIUM: htx: Skip UNUSED blocs when getting previous,next and first blocks
Christopher Faulet [Wed, 8 Jul 2026 08:38:12 +0000 (10:38 +0200)]
MEDIUM: htx: Skip UNUSED blocs when getting previous,next and first blocks

htx_get_prev_blk(), htx_get_next_blk() and htx_get_first_blk() functions
were modified to automatically skip UNUSED blocks. So now, a valid block or
NULL is returned.  In addition, htx_remove_blk() was adapted accordingly to
also skip UNUSED blocks.

Thanks to this change, the loops on HTX messages could be simplified by
removing test on UNUSED blocks, while it remains harmless.

7 weeks agoBUG/MINOR: mux-h2: Reject trailers received on a tunneled stream
Christopher Faulet [Fri, 11 Sep 2026 15:43:13 +0000 (17:43 +0200)]
BUG/MINOR: mux-h2: Reject trailers received on a tunneled stream

For a tunneled stream, the end of the HTTP message is reported just after
the headers. It is an "early" EOM: from the HTTP point of view, the message
is finished and only tunneled data may be exchanged then.

However, a HEADERS frame received after the initial one on such a stream
was still handled as a trailers block. The trailers were thus pushed in an
HTTP message already ended, leading to an invalid HTX message where blocks
are inserted after the end of the message. The stream is then aborted when
the outgoing multiplexer handles the unexpected block. So a client is able
to kill its own tunnel by sending a bogus HEADERS frame.

Such a frame is now rejected with a stream error (PROTOCOL_ERROR). The test
is performed in h2c_dec_hdrs(), at the only place where a HEADERS frame is
identified as a trailers block. This way, both directions are covered. The
aborted tunnel attempts are also covered because the early EOM was reported
in that case too.

This patch should be backported to all stable versions. Note that
h2c_report_glitch() only exists since 3.0. The corresponding line must be
removed for older versions.

7 weeks agoMINOR: htx: Add macros for the header/trailer name and value max lengths
Christopher Faulet [Thu, 17 Sep 2026 06:44:23 +0000 (08:44 +0200)]
MINOR: htx: Add macros for the header/trailer name and value max lengths

The maximum length of the name and the value of a header or a trailer were
hardcoded in the different places where they are tested. Let's define
HTX_HDR_NAME_MAX_LEN and HTX_HDR_VALUE_MAX_LEN to avoid these magic values
and document why these limits exist.

7 weeks agoBUG/MAJOR: htx: Check the header/trailer length limits when one is updated
Christopher Faulet [Thu, 17 Sep 2026 06:44:05 +0000 (08:44 +0200)]
BUG/MAJOR: htx: Check the header/trailer length limits when one is updated

The name and the value lengths of a header or a trailer are respectively
encoded on 8 and 20 bits in the block descriptor. So the name must not
exceed 255 bytes and the value must not exceed 1048575 bytes. This is
properly tested when a header or a trailer is added, since commit 3b69886f7
("BUG/MAJOR: htx: fix missing header name length check in
htx_add_header/trailer"). But no test was performed when one is updated.

htx_replace_blk_value(), used to update a part of a header or a trailer
value, may grow it above the limit. And htx_replace_header(), used to
replace a full header, does not check its arguments at all. In both cases,
the length silently overflows on the block type, changing it. For instance,
a HDR block becomes an EOH block while its value length wraps to a small
value.

Only the value length limit is reachable from the configuration, via a
"replace-header" or a "replace-value" rule, and only if "tune.bufsize" was
set above 1MB, because the block must first fit in the buffer. In that case,
a client sending a large enough header is enough to hit the issue.

So, perform the same tests than those performed when a header or a trailer
is added, instead of corrupting the message. In addition, a BUG_ON() was
added in htx_change_blk_value_len() and htx_set_blk_value_len() to catch any
other caller missing the test.

Reported by: Adam Crawford

This patch must be backported to all stable versions.

7 weeks agoBUG/MINOR: tools: Don't try to anonymize a NULL string
Christopher Faulet [Tue, 15 Sep 2026 05:36:27 +0000 (07:36 +0200)]
BUG/MINOR: tools: Don't try to anonymize a NULL string

hash_anon() function is used to anonymize a string. It is called at several
places (via HA_ANON_* macros), especially from debugging functions. It is
hard to be sure the string to hash is always defined. So let's return
immediately if it is NULL.

Among other things, this fix avoids to get a crash when the server name
created by a LUA CO-SOCKET is anonymized. Indeed, this server is created
with no name.

This patch should fix the issue #3491. It must be backported as far as 2.8.

7 weeks agoBUG/MEDIUM: htx: Fix the position returned by htx_defrag()
Christopher Faulet [Fri, 18 Sep 2026 11:20:09 +0000 (13:20 +0200)]
BUG/MEDIUM: htx: Fix the position returned by htx_defrag()

In htx_defrag(), the <new> counter was used to compute the new position of
the start-line and of the referenced block, if any. But this counter was
incremented for each block of the source message while consecutive DATA
blocks may be merged when they are copied. In this case, no new block is
created in the temporary message, so <new> was desynchronized from the real
position.

As a consequence, htx_defrag() could return a block located after the tail
of the message, thus pointing on the payload part instead of a block
descriptor. The caller then used it to copy data, at a pseudo-random offset,
corrupting the message.

htx_replace_blk_value() is the only concerned caller because it is the only
one referencing a block that may be a DATA block. htx_replace_header() and
htx_replace_stline() reference a header or a start-line, always located
before any DATA block. So, to trigger the bug, an expansion requiring a
defragmentation must be performed on a DATA block preceded by at least two
mergeable DATA blocks.

The positions are now computed from the block really created or updated. The
<new> counter is thus no longer necessary and was removed.

The flaw exists since the HTX was introduced. However the only known way to
expand a DATA block this way is the decompression filter, added in 3.5-dev.
So no released version should be affected. This patch may be backported to
all stable versions as a precaution.

7 weeks agoBUG/MINOR: ssl/ocsp: Report an error when an empty OCSP response is received
Christopher Faulet [Wed, 16 Sep 2026 07:42:35 +0000 (09:42 +0200)]
BUG/MINOR: ssl/ocsp: Report an error when an empty OCSP response is received

When the response of the OCSP update request has no payload, no OCSP
response is loaded but a successful update is reported anyway. The number of
successes is incremented, the last update date is refreshed and the entry is
reinserted in the update tree with the regular delay, as if everything was
fine.

The "Content-Type" header of the response is already checked to be
"application/ocsp-response". So an empty payload is an error. It is now
reported as an OCSP response check failure.

This patch depends on:

  * "MINOR: ssl/ocsp: Remove the useless res_payload callback of the http-client".

It may be backported with it if needed.