Hi,
HAProxy 2.4.29 was released on 2025/04/22. It added 67 new commits
after version 2.4.28.
This version catches up with the last 5 months of fixes, corresponding to
what was merged in 2.6 since 2.6.20. Again, there's nothing really
important but a lot of small stuff:
- The H1 multiplexer was only able to handle timeouts if the client or
server timeouts were defined, depending on the side. So, it was possible
to ignore client-fin/server-fin and http-keep-alive/http-request
timeouts.
- The way to deal with too many headers in received H2. The maximum
number of headers allowed in HEADERS frames on sending path was lower
than on receiving path. This could lead to report sending errors while
the message was accepted. It could be confusing. In addition, unlike
H1, the number of headers must be limited when H2 messages are sent
to comply to limitation imposed by the protocol. This limit was
increased to support headers rewriting without issue.
- On the H2 multiplexer, on server side, it was possible to send
RST_STREAM frame for streams with unassigned ID, so before the
formatting of the HEADERS frame, because the session was aborted during
the connection stage. It was an issue if this happened before the H2
PREFACE was sent because this prevent the servers to recognize it as a
H2 connection, leading to an early connection closure. We now take care
to not emit RST_STREAM frame in that case.
- Four issues with the L7 retries were fixed. First, the server status was
not adjusted at each retry, while it should be. Only the last connection
attempt was considered. Then, the buffer used to save the request to be
able to perform a L7 retry was released to early in some rare cases and
the request could be lost. It is of course unexpected and this could
lead to crash. The request state was not properly reset on L7 retry. The
request channel flag stating some data were sent was not reset on
retry. This could lead to consider a subsequent connection error as a L7
error while the request was never sent. In that case too, the request
could be lost, leading to crash. Finally, the L7 retries could be
ignored if a server abort was detected during the request forwarding
when the request was already in DONE state. In that case, the server
abort must be handled on the response analysis side to be able to
properly handle the L7 retries.
- The reason was missing in H2 responses forwarded to H1 clients while it
was stated in the configuration manual that HAProxy should add one that
matched the status code. It is now fixed.
- HAPROXY_CLI and HAPROXY_MASTER_CLI could exposed the internal sockpairs
which should be only used for the master CLI. These internal sockpairs
are now always hidden.
- The SIGINT signal could be missed by HAProxy when it was started in
background in a subshell. It is the root cause of some unexpected
timeouts with Vtest scripts. To fix the issue, the default signal
handler is registered for the SIGINT signal during init.
- A weird issue was fixed about the epoll poller. Over the last two years,
there were few reports about immediate closes spuriously happening on
connections where network captures proved that the server had not closed
at all (and sometimes even received the request and responded to it
after HAProxy had closed). The logs shown that a successful connection
was immediately reported on error after the request was sent. After
investigations, it appeared that a EPOLLUP, or eventually a EPOLLRDHUP,
can be reported by epool_wait() during the connect() but in
sock_conn_check(), the connect() reports a success. So the connection
was validated but the HUP was handled on the first receive and an error
was reported. So, to workaround the issue, we have decided to remove
FD_POLL_HUP flag on the FD during the connection establishment if
FD_POLL_ERR is not reported too in sock_conn_check(). This way, the call
to connect() is able to validate or reject the connection. At the end,
if the HUP or RDHUP flags were valid, either connect() would report the
error itself, or the next recv() would return 0 confirming the closure
that the poller tried to report. EPOLL_RDHUP is only an optimization to
save a syscall anyway, and this pattern is so rare that nobody will ever
notice the extra call to recv(). Please note that at least one reporter
confirmed that using poll() instead of epoll() also addressed the
problem, so that can also be a temporary workaround for those
discovering the problem without the ability to immediately upgrade.
- The expiration date for the task responsible for cleaning up the server
resolution status when outdated info were inherited from the state file
was fixed. "hold.timeout" was initially used. But is was not accurate,
especially because it could be set to a high value or 0. Now the
expiration date is based on the resolver "resolve" and "retry"
timeouts. In addition, when a resolver was woken up to process DNS
resolutions, it was possible to trigger an infinite loop on the
resolver's wait list because delayed resolutions were always reinserted
at the end of this list. This led the watchdog to kill the process. By
re-inserting them in front of the list fixed the issue.
- an API issue in the applets could have resulted in some shutdown or
error conditions to be missed in the future, so as a prevention it
was fixed. Turns out, after fixing this, it uncovered a bug in the
CLI's "_getsocks" handler that was causing an infinite loop during
reloads, and another one in the SPOE applet where the appled would
never shut down (neither appeared in a released version), and these
bug were also fixed.
- reloads that transfer listening sockets to the new worker process could
make the older worker consume a lot of CPU for no apparent reason for
the time it remained present. The cause was that these FDs were
registered in epoll and when a new connection arrived to the new
process, the old one would also be notified without being able to
unregister it since already closed (well-known epoll pitfall). Now
these FDs are properly unregistered after being transfered so it's
possible that some users with long-running old processes will observe
a lower CPU usage on these old processes.
- a BUG_ON() could be triggered when using filters with no http_payload
callback.
- a bug in htx_xfer_blks() could result in occasionally transfering more
blocks than requested on 32-bit platforms.
- some TLSv1.3 signature algorithms were not recognized by the
ClientHello parser which was written before TLSv1.3. The ones that
were not correctly supported were based on RSA-PSS and would have
resulted in presenting a possibly wrong certificate when both RSA and
ECDSA ones were present for the same SNI.
- disabling the send-proxy-v2 feature on a "server" line after some fields
had been enabled in the defaults section would result in an attempt to
send a PROXY-v1 line because the presence of any field was tested to
decide to send the PROXY header.
- a use-after-free condition was occasionally possible in Lua applets
handling CLI keywords, causing random crashes. It was apparently
difficult to trigger and did apparently not happen before 3.0.
- SPOE applets could be woken in loops during stopping, thus eating a lot
of CPU until the process stopped.
- usesrc clientip would accidentally include the client's port in the
hash calculation, resulting in a very low connection reuse ratio.
- leading and trailing spaces are now properly trimmed from h2 header
values. It had been observed that some rare clients keep the space
after the comma when splitting "cookie" values, causing errors between
haproxy and servers, so we can reasonably expect that this would
happen with h3 too if the same clients support both protocols.
- stick-tables could learn entries from peers with an expiration date
further in the future than what their own expire time permits, causing
entries resulting from a temporary misconfiguration to be very
difficult to evict from a cluster (e.g. mistakenly write "24d" instead
of "24h" and entries persist for 3 weeks even across reloads). Now the
entries' expiration date will be capped to the stick table's setting.
- the regsub converter, used to perform regex-based substitutions, would
check the remaining room in the buffer against the initially available
size for each replaced pattern instead of checking it against the
remaining size. This was reported by Aleandro Prudenzano of Doyensec
and Edoardo Geraci of Codean Labs, and was assigned CVE-2025-32464.
The risk is quite low since such configs are quite unlikely and in the
rare cases they will happen, the replacement will involve static
contents, thus essentially a risk of crash.
- a few issues on the peers section parser and config consistency checker
possibly causing issues or even a segfault.
- a few minor memory leaks were found in error paths (auth, _getsock)
- FCGI would always force the status to 302 when seeing a Location
header, possibly overwriting another status code.
- a pending close from the server could be forwarded to the client
despite a pending tcp-response content evaluation.
Given that 2.4 is in critical fixes status, I'd suggest 2.4 users to have
a look at it in case they estimate that they might be affected by one of
the issues fixed above, and possibly plan for an update. In any case,
please remember to at least update before reporting an issue. But so far,
no need to immediately jump on this one if what you have works well for
you.
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
Sources : https://www.haproxy.org/download/2.4/src/
Git repository : https://git.haproxy.org/git/haproxy-2.4.git/
Git Web browsing : https://git.haproxy.org/?p=haproxy-2.4.git
Changelog : https://www.haproxy.org/download/2.4/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 (3):
BUG/MINOR: backend: do not overwrite srv dst address on reuse
BUG/MINOR: h1: do not forward h2c upgrade header token
BUG/MINOR: h2: reject extended connect for h2c protocol
Aurelien DARRAGON (8):
DOC: lua: fix yield-dependent methods expected contexts
BUG/MEDIUM: pattern: prevent uninitialized reads in pat_match_{str,beg}
BUG/MINOR: stktable: fix big-endian compatiblity in smp_to_stkey()
DOC: management: rename some last occurences from domain "dns" to
"resolvers"
BUG/MINOR: cfgparse/peers: fix inconsistent check for missing peer server
BUG/MINOR: cfgparse/peers: properly handle ignored local peer case
BUG/MEDIUM: hlua/cli: fix cli applet UAF in hlua_applet_wakeup()
BUG/MINOR: hlua: fix invalid errmsg use in hlua_init()
Christopher Faulet (24):
BUG/MEDIUM: resolvers: Insert a non-executed resulution in front of the
wait list
BUG/MEDIUM: mux-h2: Don't send RST_STREAM frame for streams with no ID
BUG/MINOR: http_ana: Report -1 for %Tr for invalid response only
DOC: config: Slightly improve the %Tr documentation
BUG/MINOR: http-ana: Adjust the server status before the L7 retries
BUG/MEDIUM: mux-h2: Increase max number of headers when encoding HEADERS
frames
BUG/MEDIUM: mux-h2: Check the number of headers in HEADERS frame after
decoding
BUG/MEDIUM: http-ana: Don't release too early the L7 buffer
BUG/MEDIUM: sock: Remove FD_POLL_HUP during connect() if FD_POLL_ERR is
not set
BUG/MEDIUM: http-ana: Reset request flag about data sent to perform a L7
retry
BUG/MINOR: h1-htx: Use default reason if not set when formatting the
response
BUG/MINOR: server-state: Fix expiration date of srvrq_check tasks
BUG/MEDIUM: mux-h1: Fix how timeouts are applied on H1 connections
BUG/MINOR: spoe: Check the shared waiting queue to shut applets during
stopping
BUG/MINOR: spoe: Allow applet creation when closing the last one during
stopping
BUG/MEDIUM: spoe: Don't wakeup idle applets in loop during stopping
REGTESTS: Fix truncated.vtc to send 0-CRLF
BUG/MINOR: cli: Wait for the last ACK when FDs are xferred from the old
worker
BUG/MEDIUM: filters: Handle filters registered on data with no payload
callback
BUG/MINOR: fcgi: Don't set the status to 302 if it is already set
BUG/MINOR: tcp-rules: Don't forward close during tcp-response content
rules eval
BUG/MINOR: cli: Fix memory leak on error for _getsocks command
BUG/MINOR: cli: Fix a possible infinite loop in _getsocks()
BUG/MINOR: stats-json: Define JSON_INT_MAX as a signed integer
Dragan Dosen (1):
BUG/MINOR: server: fix the "server-template" prefix memory leak
Emeric Brun (2):
BUG/MINOR: peers: fix expire learned from a peer not converted from ms to
ticks
BUG/MEDIUM: peers: prevent learning expiration too far in futur from
unsync node
Ilia Shipitsin (1):
BUG/MINOR: namespace: handle a possible strdup() failure
Lukas Tribus (1):
DOC: option redispatch should mention persist options
Nathan Wehrman (1):
DOC: config: correct the table for option tcplog
Olivier Houchard (1):
TESTS: Fix build for filltab25.c
Valentine Krasnobaeva (6):
BUG/MINOR: cli: don't show sockpairs in HAPROXY_CLI and HAPROXY_MASTER_CLI
BUG/MINOR: signal: register default handler for SIGINT in signal_init()
BUG/MINOR: ssl: put ssl_sock_load_ca under SSL_NO_GENERATE_CERTIFICATES
BUG/MINOR: cfgparse: fix NULL ptr dereference in cfg_parse_peers
BUG/MEIDUM: startup: return to initial cwd only after
check_config_validity()
BUG/MINOR: log: fix gcc warn about truncating NUL terminator while init
char arrays
William Lallemand (5):
BUG/MINOR: ssl: can't load a separated key file with openssl > 3.0
BUG/MEDIUM: ssl: chosing correct certificate using RSA-PSS with TLSv1.3
BUG/MEDIUM: htx: wrong count computation in htx_xfer_blks()
DOC: htx: clarify <mark> parameter for htx_xfer_blks()
TESTS: ist: fix wrong array size
Willy Tarreau (14):
BUG/MEDIUM: checks: make sure to always apply offsets to now_ms in
expiration
BUG/MEDIUM: mailers: make sure to always apply offsets to now_ms in
expiration
BUG/MINOR: peers: make sure to always apply offsets to now_ms in
expiration
DOC: configuration: explain quotes and spaces in conditional blocks
BUG/MEDIUM: clock: make sure now_ms cannot be TICK_ETERNITY
BUG/MEDIUM: fd: mark FD transferred to another process as FD_CLONED
MINOR: cli: export cli_io_handler() to ease symbol resolution
DOC: config: fix two missing "content" in "tcp-request" examples
BUG/MEDIUM: sample: fix risk of overflow when replacing multiple regex
back-refs
BUG/MINOR: backend: do not use the source port when hashing clientip
DOC: config: add the missing "profiling.memory" to the global kw index
BUG/MINOR: h2: always trim leading and trailing LWS in header values
BUG/MINOR: server: check for either proxy-protocol v1 or v2 to send hedaer
BUILD: makefile: silence deprecated declarations when using OpenSSL
---