Hi,

HAProxy 3.4.1 was released on 2026/06/25. It added 43 new commits
after version 3.4.0.

This first maintenance release addresses a number of bugs accumulated since
3.4.0 was released. The most notable fixes concern external health checks,
QUIC and HTTP/3, the HTX data draining in several multiplexers, and some
SSL/TLS regressions.

Several bugs were found and fixed in the external health check feature. A
NULL-pointer dereference could occur during the tcpcheck post-config phase
when an external check was configured on a backend. The fix ensures external
checks bypass this step entirely. Related to this, the "small-buffer" option
was not guarded against external checks either, resulting in another crash
when enabled with external checks. These two issues are specific to 3.4. In
addition, when a server configured to use tune.max-checks-per-thread was
deleted while its check was still queued, a use-after-free would occur; the
fix dequeues the check before freeing the server. The "healthcheck" keyword
on default-server lines was parsed but never propagated to actual servers;
it is now properly applied. Finally, a server-level healthcheck section was
ignored when an external check was also configured at the proxy level; the
server-level healthcheck now correctly takes precedence.

Multiple bugs were fixed in the QUIC multiplexer. A potential infinite loop
was possible in qcc_io_recv() when a RESET_STREAM was received while the
stream was still in the recv_list; the loop is now correctly guarded. A
related issue where the recv loop was unnecessarily interrupted on
stream-level errors or incomplete data was also fixed. A transfer freeze
could occur after a QCS rxbuf realign operation because the next Rx buffer
was not reinserted into the QCS tree after the data move; this caused data
loss particularly on POST requests on the frontend or on most backend
transfers. Another issue in qcs_http_reset_buf() caused the entire buffer to
be reset unconditionally instead of draining only the requested amount,
which could discard unsent data when a filter was registered on the data
forwarding stage.

In the HTTP/3 layer, a bug caused malformed chunked encoding when proxying
an H3 request without Content-Length to an HTTP/1 backend. The H3 mux was
assigning the cumulative data length to the stream endpoint's known input
payload instead of incrementing it, producing incorrect chunk sizes for
multi-frame bodies. PUSH_PROMISE frames on backend connections were also not
handled properly. Additionally, a length field calculation bug was fixed in
QUIC Initial packets: when padding is added to a small packet, the varint
encoding of the Length field grows by one byte, but the length value itself
was not adjusted accordingly, causing peers to reject such packets. A
separate issue limited the QUIC backend-side rxbuf to a single buffer,
causing significant latency when retrieving large objects; this is now
corrected. Finally, xprt_qmux was missing the ->get_ssl_sock_ctx() method,
causing conn_get_ssl_sock_ctx() to return NULL during the qmux handshake and
leading to a crash when a TLS alert was received from the peer.

Several fixes were applied to the hq-interop HTTP/0.9 transcoder used for
QUIC backend debugging. Response buffer wrapping was not handled, large
responses with a full demux buffer were not properly paused, streams with
unknown payload length were incorrectly closed with RESET_STREAM instead of
FIN, and a missing buffer-full check could trigger a BUG_ON() assertion.

Three multiplexers were affected by the same draining bug: when trying to
send data to a server whose stream was closed (in error or half-closed
state), the mux claimed to have consumed the buffered data without actually
draining them.  This means the upper stream would keep trying to send these
data in a loop, potentially triggering the watchdog. The FCGI, H2, and SPOP
multiplexers were all fixed.

In the FCGI mux, an integer overflow was fixed: the unsigned 16-bits integer
used for the record length overflowed to 0 when the record length was 65535
and padding was non-zero, causing the state machine to skip consuming buffer
data and parse the remaining bytes as a new FCGI record header. This could
only happen for unknown records or for STDOUT records received after the end
of the response. CVE-2026-55203 was assigned to this overflow. It must be
noted however that the assigned severity is excessively high relative to the
actual exploitability: triggering this bug requires the FCGI backend server
itself to be compromised and to craft a malicious response with exactly
those values, which is well outside a normal threat model where the backend
is trusted.

A regression was fixed in the Lua HTTP applet: after migrating it to its own
internal buffers, the EOS flag was no longer set when the applet exited. This
caused streams with unknown payload size (i.e. with no content-length or
transfer-encoding) to block indefinitely waiting for an end-of-stream signal
that would never come.

When L7 retries were configured and the maximum retry count was reached on a
reused connection, the stream was silently closed on the client side to let
it safely retry the request instead of returning a 502 or 425 error. The
SF_SRV_REUSED flag is now ignored in this path so that the error is properly
reported to the client. Additionally, the set-headers-bin and
add-headers-bin HTTP actions were not making a copy of the sample expression
result before manipulating the HTX message; since both use the same static
trash chunk, internal defragmentation could silently overwrite the sample
data, leading to incorrect header values.

Two SSL/TLS fixes are included. With kTLS, the TLS ULP was being enabled on the
socket before the TCP connection was fully established; the Linux kernel 
requires
TCP_ESTABLISHED state for this to succeed, so the operation is now deferred
until the TLS handshake completes. With TLS 1.3 0-RTT, the early data buffer
was freed when emptied, but more early data could still arrive; the buffer is
now reset rather than freed until all early data has been received.

In the ACME client, when a CA returned an authorization with status already
"valid" (e.g. because a domain was recently validated), the auth->ready flag
was never initialized, keeping it at 0. This caused the task to wait
indefinitely for a readiness signal that will never arrive, since no
challenge was published and no external agent will ever call
challenge_ready() for that domain

Two startup bugs were fixed. Since 3.3, the PCRE2 thread-local match array
was initialized too late in the boot sequence, after lua-load, meaning that
any Lua script using a regex with PCRE2_JIT would crash. The initialization
now happens early enough to cover the Lua loading phase. Separately, thread
detection code referenced a variable that is only available when threads are
enabled, breaking the build without thread support.

A NULL pointer dereference was fixed in the HPACK table code: when the
memory pool was exhausted, hpack_dht_defrag() could return NULL and be
immediately dereferenced, crashing the worker process. CVE-2026-55204 was
assigned to this issue. Here again the assigned severity is excessively
high: the condition only arises when the process is already out of memory,
at which point the allocator failure itself is likely to cause other
problems before this specific code path is even reached. The probability of
hitting this particular NULL dereference in a production environment is
extremely thin, and a crash under memory exhaustion, while undesirable, is
not a meaningful security primitive an attacker could reliably control or
exploit.

Finally, a few minor issues were also addressed: the error message for ACL
identifier lookup failures incorrectly said "map" instead of "ACL";
ha_diag_warning() was misused to report thread creation counts, causing false
positives with -dW; a new ha_diag_notice() function was introduced to avoid
this; a relative file path for the 'h1-case-adjust-file' directive was not
resolved to an absolute path at parse time, causing failures with multiple
config files; adding a server with consistent hash balancing was broken; and
a leftover debugging memset was removed from the redirect path.

Thanks to Andrea Cocito who fixed the HTTP/3 payload length accounting issue
causing malformed chunked encoding to HTTP/1 backends, to Karol Kucharski
who fixed the kTLS regression where the TLS ULP was enabled before the TCP
connection was established, to Tristan Madani who found and fixed both the
FCGI integer overflow (CVE-2026-55203) and the HPACK NULL pointer
dereference (CVE-2026-55204) and to Thayne McCombs for his Lua documentation
fix.

And of course, thanks to everyone for your help on this release!

Please find the usual URLs below :
   Site index       : https://www.haproxy.org/
   Documentation    : https://docs.haproxy.org/
   Wiki             : https://github.com/haproxy/wiki/wiki
   Discourse        : https://discourse.haproxy.org/
   Slack channel    : https://slack.haproxy.org/
   Issue tracker    : https://github.com/haproxy/haproxy/issues
   Q&A from devs    : https://github.com/orgs/haproxy/discussions
   Sources          : https://www.haproxy.org/download/3.4/src/
   Git repository   : https://git.haproxy.org/git/haproxy-3.4.git/
   Git Web browsing : https://git.haproxy.org/?p=haproxy-3.4.git
   Changelog        : https://www.haproxy.org/download/3.4/src/CHANGELOG
   Dataplane API    : 
https://github.com/haproxytech/dataplaneapi/releases/latest
   OpenTelemetry    : https://github.com/haproxytech/haproxy-opentelemetry
   Pending bugs     : https://www.haproxy.org/l/pending-bugs
   Reviewed bugs    : https://www.haproxy.org/l/reviewed-bugs
   Code reports     : https://www.haproxy.org/l/code-reports
   Latest builds    : https://www.haproxy.org/l/dev-packages


---
Complete changelog :
Amaury Denoyelle (10):
      BUG/MEDIUM: mux_quic: prevent risk of infinite loop on recv
      BUG/MINOR: mux_quic: do not interrupt recv on error/incomplete data
      BUG/MINOR: quic: fix Initial length value in sent packets
      BUG/MINOR: server: fix add server with consistent hash balancing
      BUG/MINOR: quic: fix rxbuf settings on backend side
      BUG/MEDIUM: mux_quic: fix freeze transfer after QCS rxbuf realign
      BUG/MINOR: hq-interop: reject too big content
      BUG/MINOR: hq-interop: prevent reset if missing content-length
      BUG/MINOR: hq-interop: support full demux buf on large response
      BUG/MINOR: hq-interop: support response buffer wrapping

Andrea Cocito (2):
      BUG/MEDIUM: h3: increment unknown request payload length
      REGTESTS: quic: test H3 request without content-length

Christopher Faulet (16):
      BUG/MEDIUM: check: Skip tcpcheck post-config for external checks
      BUG/MEDIUM: check: Ignore small-buffer option when starting an external 
check
      MINOR: check: Don't dump buffers state in check traces for external checks
      BUG/MEDIUM: server/checks: Support healtcheck keyword on default-server 
lines
      BUG/MINOR: tcpcheck: Override external check if healthcheck section is set
      REGTESTS: checks: Add script for external healthchecks
      REGTESTS: Fix log matching in healthcheck-section.vtc
      BUG/MINOR: http-ana: Remove a debugging memset on redirect
      BUG/MEDIUM: http-ana: Don't ignore L7 retry errors
      BUG/MINOR: mux-h1: Properly resolve file path for 'h1-case-adjust-file'
      BUG/MEDIUM: http-act: Make a copy of the sample expr in 
(set/add)-headers-bin
      BUG/MEDIUM: hlua: Properly report EOS when http applet exits
      BUG/MEDIUM: mux-fcgi: Truly drain outgoing HTX data when the stream is 
closed
      BUG/MEDIUM: mux-h2: Truly drain outgoing HTX data when the stream is 
closed
      BUG/MEDIUM: mux-spop: Truly drain outgoing data when the stream is closed
      BUG/MEDIUM: mux-quic: Drain the given amount of data in 
qcs_http_reset_buf()

Karol Kucharski (1):
      BUG/MEDIUM: ktls: defer enabling TLS ULP on a socket until connected

Olivier Houchard (4):
      BUG/MEDIUM: threads: Fiw build when using no thread
      BUG/MEDIUM: checks: Dequeue checks on purge
      BUG/MEDIUM: h3: Properly handle PUSH_PROMISE on backend connections
      BUG/MEDIUM: ssl: Don't free the early data buffer too early

Thayne McCombs (1):
      DOC: lua: remove incorrect init tags

Tristan Madani (2):
      BUG/MINOR: hpack-tbl: add missing NULL check after hpack_dht_defrag()
      BUG/MEDIUM: mux-fcgi: fix uint16_t overflow in drl += drp

William Lallemand (1):
      BUG/MEDIUM: acme: stuck ACME task when authz is already "valid"

Willy Tarreau (6):
      BUG/MEDIUM: regex: initialize the match array earlier during boot
      BUG/MEDIUM: xprt_qmux: implement ->get_ssl_sock_ctx() to get the SSL laye
      CLEANUP: sessions: simplify the sess_priv_conns pool name
      BUG/MINOR: acl: report "ACL" not "map" in ACL ID lookup failures
      MINOR: errors: add ha_diag_notice() to report diag-level notifications
      BUG/MINOR: cpu-topo: use ha_diag_notice() to report thread creations

--
Christopher Faulet



Reply via email to