Hi,
HAProxy 3.3.13 was released on 2026/07/29. It added 78 new commits
after version 3.3.12.
All fixes brought in this release were already described i the 3.4.3
announce. So let's copy them:
* ssl/ocsp: An issue about missing lock was fixed.
The OCSP stapling callback read the cached OCSP response buffer without
any lock, while the buffer can be freed and replaced at any moment by a
"set ssl ocsp-response" CLI command, the ocsp-update task, or a reload. A
concurrent update could either crash the worker (use-after-free) or leak
freed/reused heap contents to a remote client requesting the stapled
response. The reads are now protected by the existing OCSP tree lock. This
was reported and fixes by Matt Suiche from Tolmo Inc.
* QUIC/H3 : Three bugs were fixed around QUIC/H3 stream shutdown and error
handling.
A parser desync could occur when an H3 error was detected in the middle of
a multi-frame buffer, causing the caller to remove only part of the data
and restart parsing at a wrong offset, which usually produced a new,
unrelated error or an unwarranted full connection closure. A QCS instance
could also be wrongly freed if a STOP_SENDING was still scheduled for it,
most impactful on the backend side where a server could keep emitting data
on a stream haproxy already considered closed. Finally, shutting down a
stream's read side (e.g. an aborted request) did not always send a
STOP_SENDING to the peer, so a server backend could keep sending data for
a request haproxy no longer cared about.
* fd: A deadlock when closing file descriptors of other thread groups was
fixed.
Destroying a listener while holding the listener lock could deadlock when
it needed to close an fd belonging to another thread group and another
thread was waiting on the same listener lock. This was easier to reproduce
with a suspend/resume loop on ABNS sockets. The fd is now closed using a
reference-counting scheme instead of thread isolation, avoiding the
deadlock.
* applet: An issue leading to a stale task expiration date was fixed.
When an applet was waiting for a connection (e.g. the Lua Socket applet)
and reads were re-enabled once the connection became available, this was
usually done from the connection's context, so the applet's task
expiration date was not refreshed. With small timeouts this could cause
the task to appear scheduled in the past, triggering a BUG_ON() in
sc_notify(). This was fixed by moving the changes in the applet's context.
Related to this issue, a bug about lua cosockets was fixed. the Lua
cosocket timeout being applied on the wrong side. It was applied to both
the client and server sides, while it must only apply to the server side.
* stats: Two issues about the stats page, reported by Red Hat/AISLE
Research, were fixed.
Proxies updated through the stats page while in "stats admin" mode were
not subject to the "stats scope" filtering, meaning a scope meant to
restrict which proxies are visible/actionable could be silently bypassed
on POST requests.
Separately, POST requests to the stats interface did not validate that the
Origin (or Referer) header matched the Host, which is now checked to
mitigate CSRF attacks.
* cache: A leak preventing the cache from reserving rows under "Vary" was
fixed.
When a secondary cache entry matching a request with a "Vary" header was
still incomplete (being written by another stream), the request was
forwarded without reattaching its row to the available list, leaking
it. Over a Vary-heavy workload this eventually exhausted the cache and
prevented any further row reservation.
* ssl-gencert: A memory leak on every certificate generation was fixed.
Two temporary buffers were not freed after generating a certificate on the
fly, leaking memory each time a new SNI triggered certificate
generation. This issue was reported by Red Hat/AISLE Research.
* sample/protobuf: buffer overflows after pointer-shift converters, reported
by Red Hat/AISLE Research and Charles Vosburgh, were fixed.
Several converters (protobuf/ungrpc field extraction, bytes(), ltrim())
move the sample's data pointer forward on success but did not shrink the
sample's recorded buffer capacity accordingly. A converter chained
afterwards that relies on that capacity (e.g. padding via memset()) could
then write past the end of the buffer, leading to heap corruption or a
worker crash. All the affected converters now adjust the capacity
together with the pointer.
* protobuf: A nested-path validation bypass reported by Red Hat/AISLE
Research was fixed.
The protobuf field lookup used for the protobuf()/ungrpc() converters did
not strictly enforce hierarchical boundaries, so a flat sibling field
could incorrectly satisfy a nested-path lookup (e.g. matching a root-level
field as if it were nested under a parent). The lookup was rewritten as a
strict, non-recursive path walker that correctly bounds each nesting
level.
Separately, a crash because of deprecated protobuf group wire types was
fixed. These wire types are now explicitly rejected.
* proxy/server: "show servers conn/state" was not protected against
concurrent server deletion, and the check refusing to delete a server
still attached to streams was not exhaustive.
"show servers conn/state" loops over a server list to display information
and may yield in case of a large output; if the server the dump was paused
on got deleted (e.g. via dynamic server removal) in the meantime, the
worker could crash on restart. This command now relies on the existing
watcher mechanism, already used by "show stat", to survive concurrent
deletion.
Separately, the check refusing to delete a server still attached to
streams was not exhaustive: two paths (a failed connection attempt still
referencing the server, and cookie persistence set very early) were not
covered by the existing counters, allowing a server to be deleted while
still referenced. A new per-thread-group stream counter now tracks this
reliably.
* ssl: A kTLS splicing regression and a wrongly typo'd macro were fixed.
A wrong macro name (USE_VANILLA_OPENSSL instead of HAVE_VANILLA_OPENSSL)
could let kTLS start splicing while data was still pending in OpenSSL's
internal buffers, causing data not to be properly read.
Separately, when kTLS splicing received a non-application-data record
(e.g. a TLSv1.3 KeyUpdate), splicing failed outright instead of falling
back to a regular recvmsg() long enough to consume the record and resume
splicing afterwards.
* mux-h1: The case of header names were not being adjusted on some outgoing
messages.
When a map was configured to adjust the case of some header names for
legacy applications, it was not applied to the low-level error messages
generated during th request parsing. And for all other messages,
"Sec-Websocket-Key" and "Sec-Websocket-Accept" headers, inserted by the H1
multiplexer, were also concerned.
* http-fetch: Two crashes reachable from health-check configurations were
fixed.
"res.body"/"res.hdr"/... and similar response fetches assumed the
health-check receive buffer always held an HTX message, which is only true
for actual HTTP checks; on a plain TCP check, a hostile/misbehaving server
could craft the first bytes of its reply to be misinterpreted as HTX
internal fields, causing a wild read and worker crash (or leaking
arbitrary process memory).
Separately, "capture.req.hdr"/"capture.res.hdr" only validated the upper
bound of their index argument, so a negative capture id was accepted at
boot and dereferenced an out-of-bounds array entry at runtime, crashing
the worker on the very first request.
* slz: Several issues were fixed in the SLZ library.
A stream alternating many literals in the 144-255 range with cheap
back-references could keep inflating indefinitely instead of falling
back to a stored block, exceeding the library's documented worst-case
output size by several percent. A new accounting mechanism now bounds
this overhead. Practical impact on haproxy requires tune.bufsize above
~43 kB with the default reserve.
Five small correctness fixes inherited from upstream libslz were also
backported: Avoid reading up to a few bytes past the end of very short
inputs on architectures without fast unaligned access; stop appending an
extra, misplaced block to an already-finished deflate/gzip/zlib stream
(which could corrupt the trailing checksum in ~2% of fuzzed streams); fix
the Adler32 checksum accumulator sign handling on 32-bit systems
(affecting the zlib format only); avoid an undefined-behaviour signed left
shift when assembling input words byte by byte; and use the exact bit cost
when deciding whether to emit the last literals of a block as a stored
block, avoiding compressed output slightly larger than the documented
worst case.
* peers: A heap overflow when replicating large stick-table dictionary
entries was fixed.
peer_prepare_updatemsg() never verified that a stick-table entry's
dictionary value (e.g. server_key, up to ~16 kB) actually fit in the
update message being built. Since the peers protocol is plain-text and
unauthenticated, a rogue or compromised peer could plant an oversized
entry that overflows the 16 kB trash buffer as soon as the victim
replicates ("teaches") it, confirmed as a heap-buffer-overflow write. The
function now checks the available room before encoding and fails cleanly
if it doesn't fit. This was reported and fixes by Matt Suiche from Tolmo
Inc.
* resolvers: Possible conflicts between the internal backend used for the
"default" resolvers sections and regular backends with the same name were
fixed.
Since a "default" resolvers section is now created automatically, a
same-named internal TCP backend proxy also appeared, and a config using
"default_backend default" could either fail with a confusing "mode"
error or silently route real traffic to the internal resolvers backend.
Resolvers no longer need to be looked up by name in the proxies index,
so they are no longer indexed there. Fixes GitHub issue #3445.
* server-template: Duplicated entries created from a "server-template" line
did not properly inherit several settings that are only initialized once
for the base line by srv_settings_init().
The <alt_proto> field, used to select an alternative protocol such as
MPTCP, was not copied to server-template duplicates, resulting in the
wrong protocol being selected on connect. The "check-reuse-pool" flag,
forcefully enabled for rHTTP servers, was not propagated either, causing
health checks to always fail on rHTTP server-template entries. And the
QUIC transport was never set up for these duplicates at all, since it was
only performed in _srv_parse_init(), preventing any QUIC communication on
server-template entries with a quic4/6 address. All three settings are
now properly duplicated/initialized in srv_settings_cpy(), or moved to a
callback that also runs for duplicates. A related improvement makes
parsing error messages usable for server-template lines, which used to
show "server-template be/(null)" instead of the template's name.
* cebtree: A bug in the duplicate-key detection of the lookup shortcut was
fixed.
It could make a lookup return the wrong entry among a list of duplicates,
or make the walk to the previous/next duplicate fail to find it, as soon
as the tree was deep enough to enter the shortcut path. This affects the
cebis_tree used since 3.3 to index server names (conf.name), where it
could sometimes let a duplicate server name go undetected.
And, as usual, the bunch of minor fixes here and there, mainly raised during
AI-assisted code reviews. Most were never noticed:
* HTX API: Some bugs about how the HTX API was used were fixed here and
there.
* http-act: Double-frees and a couple of state bugs on parsing errors were
fixed.
* http-fetch/http/http-ana/http-htx: Few out-of-bounds reads were fixed.
* h1: The right error offset is now reported for an authority/Host mismatch
when "accept-unsafe-violations-in-http-request" option is enabled
* http-conv: The last input character could be lost when calling url-dec
converter, when the input buffer was full. This was fixed by failing the
converter in that case.
* http-rules: A crash on error path when parsing "set-cookie-fmt" redirect
rule was fixed.
* mux-h1: An extra 200ms delay was observed on some H2-to-H1 messages
because the end of the message was not always properly detected. This
case is now properly handled.
* sample: Two edge cases in be2hex() and bytes() were fixed.
* shctx: An issue in the shctx was fixed. This notably broke revalidation of
cached responses whose ETag was stored beyond the first block, causing a
full 200 response to be served instead of a 304.
* ssl: "show ssl sni -f <frontend>" command was silently falling back to the
first proxy when the given frontend did not exist. An error is now
reported.
* stream: Custom per-request timeouts/retries set on listeners via
"set-timeout" and "set-retries" action are now preserved, except when the
stream is routed to a diffent backend. The configuration manual was
updated to document this case.
Otherwise, one change was backported:
* ssl: tune.ssl.keyupdate-rate-limit global directive was added to mitigate
costly TLSv1.3 KeyUpdate floods.
Processing a TLSv1.3 KeyUpdate is CPU-expensive and there is normally
little reason to receive many of them. This new tunable caps the number
of KeyUpdates accepted per second (default 100, 0 disables the limit). Two
small follow-up fixes came along with it: the internal event code used
when closing a connection because of excessive KeyUpdates is now reported
at the right place, and a deprecated OpenSSL macro name used to detect the
KeyUpdate handshake message type was replaced with its numeric value to
avoid a build failure.
Thanks 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.3/src/
Git repository : https://git.haproxy.org/git/haproxy-3.3.git/
Git Web browsing : https://git.haproxy.org/?p=haproxy-3.3.git
Changelog : https://www.haproxy.org/download/3.3/src/CHANGELOG
Dataplane API :
https://github.com/haproxytech/dataplaneapi/releases/latest
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 (14):
BUG/MEDIUM: h3: fix parser desync on error with multiple frames
BUG/MINOR: mux_quic: prevent multiple STOP_SENDING emission per stream
BUG/MEDIUM: mux_quic: do not free QCS if STOP_SENDING to sent
BUG/MINOR: ssl: fix proxy lookup for show ssl sni
DOC: fix typo in "del ssl ech" command
BUG/MEDIUM: proxy: protect "show servers ..." against server deletion
MINOR: mux_quic: use separate error code for STOP_SENDING
MEDIUM: mux-quic: extend shut to app proto layer
BUG/MEDIUM: mux_quic: complete stream shutdown for read channel
BUG/MINOR: quic: ignore STREAM after MUX closure on BE side
MINOR: server: improve parsing error for server-template
BUG/MINOR: server: fix QUIC on server-template
BUG/MINOR: server: duplicate server alt_proto in srv_settings_cpy()
BUG/MINOR: server: fix check reuse-pool in srv_settings_cpy()
Christopher Faulet (17):
BUG/MINOR: sample: Fix a possible underflow on be2hex for large chunk size
BUG/MINOR: stream: Fix custom timeouts initialization when setting backend
BUG/MINOR: stream: Fix custom max-retries initialization when setting
backend
BUG/MINOR: http-conv: Make url-dec failed if no space for trailing null
byte
BUG/MINOR: hlua: Apply socket timeout on server side only
REORG: stconn: Move sc_chk_rcv() from sc_strm.h to stconn.c
BUG/MEDIUM: applet: Reenable reads in applet context if requesting a
connection
BUG/MINOR: mux-h1: Don't delay send if message with c-l was fully sent
BUG/MEDIUM: sample: Adjust sample size capacity after pointer shift for
bytes()
BUG/MEDIUM: sample: Adjust sample size capacity after pointer shift for
ltrim()
BUG/MINOR: sample: Fix bytes() when length it greater than remaining data
REORG: h1-htx: Move h1 headers map in h1-htx
BUG/MEDIUM: mux-h1: Always adjust case for all outgoing headers as
expected
BUG/MEDIUM: http-fetch: don't parse a non-HTTP check buffer as an HTX
message
BUG/MINOR: htx: Perform raw copy for messages of same size in
htx_copy_msg()
BUG/MINOR: htx: Transfer HTX_FL_EOM flag on success in htx_append_msg()
BUG/MINOR: http-rules: fix release of a failed "set-cookie-fmt" redirect
rule
Frederic Lecaille (3):
BUG/MEDIUM: protobuf: adjust sample size capacity after pointer shift
BUG/MEDIUM: protobuf: fix nested path bypass in field lookup
REGTESTS: protobuf: add regression test for nested vs flat paths
Matt Suiche (3):
BUG/MEDIUM: peers: check the available room before encoding dict values
BUG/MEDIUM: sample: reject the deprecated protobuf group wire types
BUG/MAJOR: ssl/ocsp: lock the OCSP response around reads in the stapling
callback
Maxime Henrion (4):
BUG/MINOR: shctx: fix shctx_row_data_get() when offset exceeds a block
MINOR: shctx: clamp shctx_row_data_get() reads against the offset
DOC: stats: document the per-proxy byte count fields in the CSV list
BUG/MEDIUM: cache: reattach the row when a secondary entry is incomplete
Olivier Houchard (11):
BUG/MEDIUM: fd: Fix a deadlock when closing other tgroups fds
BUG/MEDIUM: stats: Ensure that Origin is valid on POSTs
DOC: stats: Document that stats admin is vulnerable to a CSRF attack
BUG/MEDIUM: ssl-gencert: Don't forget to free memory when done
BUG/MEDIUM: server: Properly check for streams before deletion
BUG/MEDIUM: ssl: Spell HAVE_VANILLA_OPENSSL correctly
BUG/MEDIUM: ssl: Handle non-application data record while splicing
MEDIUM: ssl: Add a way to rate-limit TLSv1.3 KeyUpdate
DOC: ssl: Document tune.ssl.keyupdate-rate-limit
BUG/MEDIUM: ssl: Put CO_ER_SSL_KEYUPDATE at the right place
BUILD: ssl: Do not use SSL3_MT_KEY_UPDATE, hardcode 24 instead
Willy Tarreau (26):
MINOR: stats: factor the proxy vs scope check into its own function
BUG/MEDIUM: stats: subject "stats admin" accesses to "stats scope"
filtering
BUG/MINOR: resolvers: do not index resolvers names in the proxies
DEBUG: fd: catch access attempts to closed FDs
BUG/MINOR: http-htx: fix the length moved when removing a header value
BUG/MEDIUM: http-fetch: reject a negative capture id in
capture.{req,res}.hdr
BUG/MINOR: http: fix an out-of-bounds read in http_get_host_port() on
empty host
BUG/MINOR: http-htx: check the trash allocation in
http_scheme_based_normalize()
BUG/MINOR: h1: report the right error position on authority/host mismatch
BUG/MINOR: h2: don't use a block pointer to roll back a partial HTX
conversion
BUG/MINOR: h3: don't use a block pointer to roll back a partial HTX
conversion
BUG/MINOR: http-ana: fix a one-byte over-read in the client-side cookie
parser
BUG/MINOR: http-act: fix a double free of the regex on a rule parsing
error
BUG/MINOR: http-act: fix a double free of the map reference on a parsing
error
BUG/MINOR: http-act: restore the response buffer state in the early-hint
action
BUG/MINOR: http-act: reject a negative capture id in the capture actions
BUG/MINOR: http-htx: check the strdup() of the "lf-string" http reply
argument
BUG/MINOR: slz: do not read past the end of the input around the match
loop
CLEANUP: slz: fix the documented worst case size of flush() and finish()
BUG/MINOR: slz: use the exact switch cost for the last literals of a block
BUG/MEDIUM: slz: bound the bits wasted by the 9-bit literals
BUG/MINOR: slz: do not append a block to an already finished stream
BUG/MINOR: slz: fix the adler32 accumulators signedness on 32-bit
BUG/MINOR: slz: avoid undefined shifts when building the word byte by byte
CLEANUP: slz: clarify that the size promise applies to the stream, not to
a call
IMPORT: cebtree: private: fix the duplicate detection in the lookup
shortcut
--
Christopher Faulet