Hi,
HAProxy 3.3.7 was released on 2026/04/23. It added 118 new commits
after version 3.3.6.
This version brings a significant number of bug fixes, more than usual
in part thanks to reports from multiple AI-assisted scans that found
bugs in diverse places. A few of these bugs were ranked as major since
they may have stability or even security impacts depending on configs
and deployments:
- a severe issue was found in the compression library (slz) where
specially crafted patterns with tune.bufsize above 17408 or
tune.maxrewrite below 964 (both non-default) could cause output
buffer overflows due to the overhead exceeding the promised
worst-case growth bound of 5 bytes and reach up to 1/16 of the
input contents. Given that the compression output is hardly
controllable, and the canaries at the end of the pools will catch
this at release time, the risk of exploitation by a hostile server
is close to zero, however it will cause repeated crashes if such a
crafted file is present on a server and regularly downloaded. A
workaround consists in keeping tune.maxrewrite at least 1/16 of
tune.bufsize or just not changing them since the defaults are safe.
A CVE was requested two weeks ago for this one, I'll mention it when
it arrives.
- HTTP/2 incomplete transfer detection was missing for HEADERS frames
carrying END_STREAM. When relayed to an HTTP/1.1 server that
responds before the end of the transfer, this can result in bytes
of the next request over the same connection to be ignored. Most of
the time it will cause the connection to be dropped due to an
unparsable request, but when combined with "http-reuse never", or
on totally idle servers, the client could expect the second request
to reuse the same connection and perform a content smuggling attack
that would allow to pass an unverified request to a server. For
those who can't upgrade, a temporary workaround is to disable
HTTP/2 by specifying "alpn http/1.1" on bind lines and adding
"disable-h2-upgrade" in HTTP frontends. A CVE will be requested for
this one.
- HTTP/1.1 bodyless messages announcing a non-null Content-Length did
not force close mode on the backend, potentially causing
desynchronisation between HAProxy and the server in conjunction
with other bugs.
- FCGI record length truncation with large bufsize (>=65544) could
enable request smuggling into PHP-FPM since the 16-bit
content_length field silently truncated to 65535 bytes.
- an unvalidated SNI name_len field in ClientHello could cause OOB
heap reads of up to 65KB via XXH3, smp_dup(), and log-format leaks
on any TCP frontend using req.ssl_sni, possibly causing crashes when
used.
- ECDSA JWT signatures with ES256/384/512 could cause a heap overflow
of ~14 bytes in the DER conversion before verification.
- a number of CLI commands were mistakenly missing the "admin" level
check, allowing a non-privileged CLI to run them. This includes
"acme", "add/set/del/show ssl ech", "tls-keys show/set", "ocsp
set/update/show", "get/add/del/clear/commit/set map/acl". These
commands now cause a deprecation warning to be emitted in logs for
versions before 3.3, and are rejected in 3.3 and above. The fix
consists in appending "level admin" at the end of the "stats socket"
line in the configuration.
- the scheduler was found to suffer from a flaw affecting certain
tasks and tasklets which can mutually wake each other up multiple
times in the same loop iteration, confiscating CPU bandwidth from
other tasks and abnormally increasing latencies. A loop counter is
now tracked per task and the queue processing stops when a task is
seen having already run, indicating that all tasks after it have
already been processed. Self-waking tasklets are no longer punished
by sending them to the BULK queue (this happened with yielding
tasks), and scheduler budgets were readjusted to match these
changes. Additionally, tasklets are no longer automatically
requeued into the current queue (that was a mistake that caused
them to be marked as self-waking). Overall the observed average and
peak latencies are now better, especially under heavy attacks.
- HTTP/2: a limited frames processing budget was added for HTTP/2
(tune.h2.be.max-frames-at-once, tune.h2.fe.max-frames-at-once,
tune.h2.fe.max-rst-at-once) to prevent latency from large frame
floods. These changes significantly improve processing latency
under heavy H2 RST flood attacks and overall average loop times.
Some tests showed that the maximum impact of some floods could be
divided by 20 to 50 by just setting "tune.h2.fe.max-rst-at-once 1".
- Lua's httpclient headers conversion accepted more than 101 headers
without bound checking, causing a stack buffer overflow reachable
from any Lua action/task/service.
- Lua had a format-string vulnerability in pattern reference error
paths, enabling ASLR/canary leaks or arbitrary writes via %n.
- peers dictionary cache updates accepted an unvalidated entry id as
array index, allowing OOB heap writes at attacker-controlled
offsets.
- a race condition in task scheduling could cause a BUG_ON() when
task_schedule() sets expire while the task is already running. Very
rare but reported by users (e.g. server configured with slowstart
updating its weight at the same moment it goes up).
- Lua had a use-after-free of HTTP reason strings managed by Lua's GC
between set_status() and start_response(), potentially leaking adjacent
information from memory.
- the regsub sample function could leak ~9-50KB of stale heap data
when back-reference expansion overflowed the output buffer.
- SPOE decode_varint() had no iteration cap, allowing pointer
arithmetic to wrap and dereference memory ~64KB before the
allocation, causing SIGSEGV or parser confusion.
- DNS AAAA additional records handling leaked ~580 bytes per response
due to a leftover break statement after a switch-to-if/else
conversion.
- HTTP/1 bodyless responses (HEAD, 204, 304) were subject to a ~200ms
TCP cork delay because MSG_MORE was unconditionally set when
inherited from the upper layer. This notably affected H1 frontend
to H2 backend setups.
- zero-copy forwarding was not disabled when draining the request
after an early response, preventing regular receives.
- HTTP/2 connection liveness checks used conn->owner to decide if a
connection was dead, which caused premature connection disposal
when owner was NULL (fully idle connections after the last stream
left), leading to connections being closed after each stream at low
loads. Also, a fix introduced in 3.3.5 to report pending errors
earlier was reverted since no longer necessary after recent HTX
fixes.
- the H1 null-chunk emission condition for bodyless messages was
wrong, with the server-side flag being tested during request
processing. For example a chunked response to a HEAD request could
be sent without the null chunk. Additionally, the trailer skip test
for chunked messages had its flags inverted between client and
server sides.
- HTX defrag used an incorrect function to change part of a block
value when data expanded, causing old block data to become
inaccessible after the move.
- CLI commands with too-big payloads caused an infinite parse loop
instead of returning an error, leaving the client stuck in prompt
state.
- in sample expressions, less common HTTP methods (PATCH etc.) are
represented by both an enum and a string. The string part was not
handled correctly in sample duplication functions, resulting in
their contents appearing empty when trying to fetch the method.
- peers expired entries were trashed later than expected after
fullresync because tree reordering didn't move them back in past.
- ACME had multiple small issues: resource leaks in x509 request
generation (X509_NAME, str_san, X509_REQ, extensions), a leak of
ext_san on insertion failure, wrong error messages for duplicate
sections and argument counts, wrong argument check in 'acme renew',
auth->dns leak in context destroy, errmsg leak from unconditional
memprintf, DER buffer leak on base64url error path, atol() replaced
with bounded __strl2uic() for retry-after parsing, task allocation
leaked on failure, and a case where the client would not skip
already-valid challenges causing cert issuance to fail. ACME now
avoids passing NULL into format strings.
- SPOE applet could hit BUG_ON() when copying frames into the context
buffer without first allocating it. This was revealed by large
buffer changes.
- QUIC had several fixes: counters used on backend side referenced
frontend counters (wrong stats for pure backends), HTML stats
missing QUIC/H3 backend capability display, missing app ops init
during backend 0-RTT session cache reuse failure causing mux crash,
incompatible frames now properly close the connection with
PROTOCOL_VIOLATION instead of being silently dropped, and stream
IDs are now limited to 2^62 per the QUIC spec. QPACK varint
decoding is now also limited to 62-bit, and had a risk of 1-byte
OOB reads on truncated streams, which could cause incorrect header
decoding.
- dynamic servers now default to "sni-auto" and support "no-sni-auto" and
"no-check-sni-auto" configuration keywords.
- tcpcheck had several minor fixes: the parsing context for error
reporting would wrongly be reported as server instead of TCP check,
and using HTTP sample fetch functions would needlessly cause an
HTTP transaction to be allocate for streams created on these
proxies.
- H2 protocol error counting was improved: trailer parse failures and
stream rejection on parsing error now properly count as protocol
errors. Previously they would be reported as internal errors. The
parsing error counter on the connection and on the proxy are now
updated. The tune.h2.log-errors setting introduced in 3.3.5 really
works now (previously no errors were logged for any value except
"none"). PRIORITY frame exclusive bit was properly masked from the
stream ID check. shared_rx_bufs are now freed on H2 init failure,
otherwise out of memory could cause memory leaks.
- the stream connector (stconn) now sets a flag on the stream
endpoint descriptor when the upper stream has started, which allows
the H2 mux to detect whether an RST_STREAM applies to an already
started stream or to one just seen, and report glitches in this
later case, as it's symptomatic of an attack.
- "compression direction response" preserved the request flag instead
of clearing it during parsing, making it impossible to disable
request compression if it was earlier enabled.
- log-format sample fetch dependencies were refined: response and
body dependencies are now considered so that timestamps, byte
counts and volatile response headers are properly waited for. This
is important when trying to log "req.bytes_in" or "res.bytes_in",
which would previously not cause the log to wait till the end of
the transfer, contrary to the %U/%B log-format aliases.
- config: a few argument parsing errors in conditional expressions
used in ".if" could be misreported and even cause a crash during
the parsing. Also, a few keywords relying on warnif_misplaced_*
didn't check the return value and didn't count emitted warnings as
warnings.
- abortonclose now only considers client aborts, not stream aborts,
preventing yielding rules from being interrupted while the client
still waits.
- stats-file: inconsistent shared clocks when resuming from
shm-stats-file are detected and corrected (adjusting when offset
>60s). A future improvement that is still under validation in 3.4
will even allow to remove that remaining limiation. GUID truncation
for shm-stats-file objects was fixed to use full 128 chars.
- the accept() error messages for ENFILE and ENOMEM now print actconn
instead of global.maxsock which doesn't reflect system limits.
- the 'show proc' command now displays old workers in the correct order
(newest first).
- a stream created from healthchecks was not flagged as a backend
connection. It doesn't seem to have had impacts, though maybe some error
counters could have been incorrectly reported.
- threads: setting nbthreads above the maximum number of threads per group
(64 or defined at build time) would silently lower the number to the
number of threads fitting in the number of groups. Now it will properly
adjust the number of groups. Those running with "nbthreads 128" were
likely limited to 64 without noticing for example, and will now have
128 threads running as if nbthreads hadn't been forced and they'd let
the CPU topology code do its job.
Finally there were also a few very minor fixes such as the "pause"
action error message, an extra ']' in log-format error message, a wrong
check in the opentracing group filter parser, and an adjustment to
avoid suspecting uninitialized variable in code analysis. The rest is a
few doc updates, CI and regtest updates.
While I usually suggest to update only if needed, here I would say that
the number of areas covered by fixes is too broad to precisely know if
some affect your usage or not, so please just update your version.
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
Willy
---
Complete changelog :
Amaury Denoyelle (10):
BUG/MINOR: proxy: detect strdup error on server auto SNI
BUG/MINOR: server: set auto SNI for dynamic servers
BUG/MINOR: server: enable no-check-sni-auto for dynamic servers
BUG/MINOR: quic: fix counters used on BE side
BUG/MINOR: quic/h3: display QUIC/H3 backend module on HTML stats
MINOR: ncbmbuf: improve itbmap_next() code
BUG/MINOR: quic: close conn on packet reception with incompatible frame
DOC: configuration: mention QUIC server support
BUG/MINOR: quic: fix documentation for transport params decoding
BUG/MINOR: mux_quic: limit avail_streams() to 2^62
Aurelien DARRAGON (5):
BUG/MINOR: stats-file: manipulate shm-stats-file heartbeat using unsigned
int
BUG/MEDIUM: stats-file: detect and fix inconsistent shared clock when
resuming from shm-stats-file
DOC: config: fix ambiguous info in log-steps directive description
BUG/MINOR: counters: fix unexpected 127 char GUID truncation for
shm-stats-file objects
BUG/MINOR: task: fix uninitialised read in run_tasks_from_lists()
Christopher Faulet (23):
Revert "BUG/MEDIUM: mux-h2: make sure to always report pending errors to
the stream"
DOC: config: Fix alphabetical ordering of proxy options
DOC: config: Fix alphabetical ordering of external-check directives
DOC: config: Add missing 'status-code' param for 'http-check expect'
directive
DOC: config: Reorder params for 'tcp-check expect' directive
BUG/MEDIUM: spoe: Acquire context buffer in applet before consuming a
frame
BUG/MINOR: config: Properly test warnif_misplaced_* return values
BUG/MINOR: http-ana: Only consider client abort for abortonclose
BUG/MINOR: stconn: Always declare the SC created from healthchecks as a
back SC
BUG/MINOR: tcpcheck: Remove unexpected flag on tcpcheck rules for httchck
option
BUG/MINOR: tcpcheck: Don't enable http_needed when parsing HTTP samples
BUG/MINOR: tcpcheck: Use tcpcheck context for expressions parsing
BUG/MEDIUM: mux-h1: Disable 0-copy forwarding when draining the request
BUG/MEDIUM: samples: Fix handling of SMP_T_METH samples
BUG/MEDIUM: cli: Properly handle too big payload on a command line
BUG/MEDIUM: htx: Fix function used to change part of a block value when
defrag
BUG/MEDIUM: htx: Don't count delta twice when block value is replaced
BUG/MINOR: mux-h1: Fix condition to send null-chunk for bodyless message
BUG/MINOR: mux-h1: Fix test to skip trailers from chunked messages
DOC: config: Fix a typo for "external-check" directive
REGTESTS: Never reuse server connection in jwt/jws_verify.vtc
REGTESTS: Never reuse server connection in
server/cli_delete_dynamic_server.vtc
BUG/MEDIUM: mux-h1: Force close mode for bodyless message announcing a C-L
Cody Ohlsen (1):
BUG/MEDIUM: mux-h1: Don't set MSG_MORE on bodyless responses forwarded to
client
David Carlier (1):
BUG/MEDIUM: acme: fix multiple resource leaks in acme_x509_req()
Egor Shestakov (2):
BUG/MINOR: sock: adjust accept() error messages for ENFILE and ENOMEM
BUG/MINOR: acme: don't pass NULL into format string
Emeric Brun (1):
BUG/MEDIUM: peers: trash of expired entries delayed after fullresync
Frederic Lecaille (2):
BUG/MINOR: qpack: fix 62-bit overflow and 1-byte OOB reads in decoding
BUG/MINOR: quic: missing app ops init during backend 0-RTT sessions
Greg Kroah-Hartman (11):
BUG/MEDIUM: payload: validate SNI name_len in req.ssl_sni
BUG/MEDIUM: jwt: fix heap overflow in ECDSA signature DER conversion
BUG: hlua: fix stack overflow in httpclient headers conversion
BUG/MINOR: hlua: fix stack overflow in httpclient headers conversion
BUG/MINOR: hlua: fix format-string vulnerability in Patref error path
BUG/MINOR: peers: fix OOB heap write in dictionary cache update
BUG/MINOR: resolvers: fix memory leak on AAAA additional records
BUG/MINOR: spoe: fix pointer arithmetic overflow in spoe_decode_buffer()
BUG/MINOR: sample: fix info leak in regsub when exp_replace fails
BUG/MEDIUM: mux-fcgi: prevent record-length truncation with large bufsize
BUG/MINOR: hlua: fix use-after-free of HTTP reason string
Ilia Shipitsin (2):
CI: github: fix tag listing by implementing proper API pagination
reg-tests/ssl/ssl_dh.vtc: fix syntax error
Mia Kanashi (2):
BUG/MINOR: acme: fix incorrect number of arguments allowed in config
BUG/MEDIUM: acme: skip doing challenge if it is already valid
Miroslav Zagorac (1):
BUG/MINOR: ot: fixed wrong NULL check in flt_ot_parse_cfg_group()
Olivier Houchard (4):
BUG/MEDIUM: tasks: Make sure we don't schedule a task already running
BUG/MINOR: h2: make tune.h2.log-errors actually work
BUG/MINOR: h2: Don't look at the exclusive bit for PRIORITY frame
BUG/MINOR: H2: Don't forget to free shared_rx_bufs on failure
Tim Duesterhus (1):
BUG/MINOR: log: Fix error message when using unavailable fetch in logfmt
William Lallemand (23):
BUG/MINOR: mworker: fix sort order of mworker_proc in 'show proc'
BUG/MINOR: acme: leak of ext_san upon insertion error
BUG/MINOR: acme: wrong error when checking for duplicate section
BUG/MINOR: acme/cli: wrong argument check in 'acme renew'
BUG/MINOR: acme: acme_ctx_destroy() leaks auth->dns
BUG/MINOR: acme: wrong labels logic always memprintf errmsg
BUG/MINOR: acme: free() DER buffer on a2base64url error path
BUG/MINOR: acme: replace atol with len-bounded __strl2uic() for
retry-after
BUG/MINOR: acme/cli: fix argument check and error in 'acme
challenge_ready'
BUILD: tools: potential null pointer dereference in dl_collect_libs_cb
BUG/MINOR: ech: permission checks on the CLI
BUG/MINOR: acme: permission checks on the CLI
BUG/MINOR: acme: fix task allocation leaked upon error
BUG/MEDIUM: ssl/cli: tls-keys commands warn when accessed without admin
level
BUG/MEDIUM: ssl/ocsp: ocsp commands warn when accessed without admin level
BUG/MEDIUM: map/cli: map/acl commands warn when accessed without admin
level
BUG/MEDIUM: ssl/cli: tls-keys commands are missing permission checks
BUG/MEDIUM: ssl/ocsp: ocsp commands are missing permission checks
BUG/MEDIUM: map/cli: CLI commands lack admin permission checks
REGTESTS: ssl: mark ssl_dh.vtc as broken
SCRIPTS: build-vtest: allow to set a TMPDIR and a DESTDIR
CI: VTest build with git clone + cache
CI: github: only enable OS X on development branches
Willy Tarreau (29):
MEDIUM: sched: do not run a same task multiple times in series
MINOR: sched: do not requeue a tasklet into the current queue
MINOR: sched: do not punish self-waking tasklets anymore
MEDIUM: sched: do not punish self-waking tasklets if TASK_WOKEN_ANY
MEDIUM: sched: change scheduler budgets to lower TL_BULK
MINOR: mux-h2: assign a limited frames processing budget
BUILD: sched: fix leftover of debugging test in single-run changes
MINOR: stconn: flag the stream endpoint descriptor when the app has
started
MINOR: mux-h2: report glitches on early RST_STREAM
SCRIPTS: git-show-backports: list new commits and how to review them with
-L
BUG/MINOR: cfgcond: properly set the error pointer on evaluation error
BUG/MINOR: cfgcond: always set the error string on openssl_version checks
BUG/MINOR: cfgcond: always set the error string on awslc_api checks
BUG/MINOR: cfgcond: fail cleanly on missing argument for "feature"
BUG/MINOR: http-act: fix a typo in the "pause" action error message
BUG/MAJOR: slz: always make sure to limit fixed output to less than worst
case literals
BUG/MINOR: threads: properly set the number of tgroups when non using
policy
BUG/MEDIUM: mux-h2: ignore conn->owner when deciding if a connection is
dead
BUG/MINOR: log: consider format expression dependencies to decide when to
log
MINOR: sample: make RQ/RS stats available everywhere
BUG/MINOR: sample: adjust dependencies for channel output bytes counters
BUG/MAJOR: sched: protect task->expire on 32-bit platforms
BUG/MINOR: mux-h2: count a protocol error when failing to parse a trailer
BUG/MINOR: mux-h2: count a proto error when rejecting a stream on parsing
error
BUG/MINOR: log: also wait for the response when logging response headers
BUG/MINOR: compression: properly disable request when setting response
BUG/MINOR: debug: properly mark the entire libs archive read-only
BUG/MINOR: server: fix a possible leak of an error message in dynamic
servers
BUG/MAJOR: mux-h2: detect incomplete transfers on HEADERS frames as well
---