]> git.ipfire.org Git - thirdparty/haproxy.git/log
thirdparty/haproxy.git
29 hours agoBUILD: makefile: search for Lua 5.5 as well
Willy Tarreau [Mon, 1 Jun 2026 16:44:04 +0000 (18:44 +0200)] 
BUILD: makefile: search for Lua 5.5 as well

Support for Lua 5.5 was brought in 3.4-dev2 with commit 1c0f781994
("MINOR: hlua: Add support for lua 5.5") but the Makefile doesn't look
for it, which can be quite confusing on recent distros which start to
ship with it. Let's add it to the looked up names.

30 hours agoBUG/MINOR: tasks: Increase the right niced_task counter
Olivier Houchard [Mon, 1 Jun 2026 16:17:50 +0000 (18:17 +0200)] 
BUG/MINOR: tasks: Increase the right niced_task counter

In __task_wakeup(), for a niced task, we don't always want to increase
the niced_task counter of the running thread's thread group, if we are
waking up the task of another thread, who belongs to a different thread
group, then we want to increment that thread group's counter instead, as
that's the one that will get decremented later.
So just increase the counter for the target thread'd thread group,
instead of using tg_ctx.
The impact is probably pretty minor, niced task shared amongst thread
are not very common, and the impact would mostly mean we'd run more/less
tasks in one run of process_runnable_tasks() than expected.
This should be backported as far as 2.8.

32 hours agoBUG/MEDIUM: leastconn: Unlock the write lock on allocation failure
Olivier Houchard [Mon, 1 Jun 2026 13:52:21 +0000 (15:52 +0200)] 
BUG/MEDIUM: leastconn: Unlock the write lock on allocation failure

When we fail to allocate a new tree element, we're still holding the
write lock, so we should do an write unlock, not a read unlock, or the
lock will get corrupted and most likely this will end in a deadlock.

This should be backported up to 3.2.

32 hours agoBUG/MINOR: mux-spop: Fix possible off-by-one OOB read in spop_get_varint()
Christopher Faulet [Mon, 1 Jun 2026 13:25:32 +0000 (15:25 +0200)] 
BUG/MINOR: mux-spop: Fix possible off-by-one OOB read in spop_get_varint()

In spop_get_varint(), -1 is returned if there is not enough data in the
buffer to decode the variable integer. However a strict comparison agasint
b_data() was performed, which is wrong. A failure must be reported if the
index is greater or equal to b_data().

This patch must be backported as far as 3.2.

32 hours agoBUG/MINOR: applet: Commit changes into input buffer after sending HTX data
Christopher Faulet [Mon, 1 Jun 2026 13:16:35 +0000 (15:16 +0200)] 
BUG/MINOR: applet: Commit changes into input buffer after sending HTX data

After sending HTX data to an applet, htx_to_buf() must be called on the
applet buffer to commit changes (and possibly to reset the buffer if it is
empty). This was performed on the output buffer while it should in fact be
performed on the input buffer. So let's fix it.

This patch must be backported as far as 3.0.

33 hours agoBUG/MEDIUM: ssl: Make sure the alpn length is small enough
Olivier Houchard [Mon, 1 Jun 2026 13:04:09 +0000 (15:04 +0200)] 
BUG/MEDIUM: ssl: Make sure the alpn length is small enough

When the check for server hash was introduced to make sure we're using
the right alpn, the logic to store the new alpn was flawed. We should
always check that the new alpn length is small enough to fit in the
buffer, no matter if the server hash is not the same or not. So always
check the length first, and only check if the alpn or the server changed
after.
This should be backported whenever commit
de3f245df032073dec134f5d0f597d73a7b2575d has been backported.

33 hours agoMINOR: debug: add -dA to dump an archive of all dependencies
Willy Tarreau [Fri, 29 May 2026 09:39:05 +0000 (11:39 +0200)] 
MINOR: debug: add -dA to dump an archive of all dependencies

This adds "-dA[file]" on the command line, which dumps an archive of all
dependencies detected at runtime into the designated file in tar format.
This is equivalent to "set-dumpable libs", but instead of keeping the libs
in memory, it dumps them into a file. This may be used after a core dump,
in order to provide all necessary libraries to developers to permit them
to exploit the core. This may not be available on all operating systems.

33 hours agoMINOR: deinit: release the in-memory copy of shared libs
Willy Tarreau [Fri, 29 May 2026 09:20:12 +0000 (11:20 +0200)] 
MINOR: deinit: release the in-memory copy of shared libs

When shared libs were loaded via "set-dumpable libs", better release
them upon deinit, it will make valgrind happier. For this we now have
a new function free_collected_libs() in tools.c and call it in deinit().

38 hours agoBUG/MEDIUM: htx: Fix headers rollback on partial copy in htx_xfer()
Christopher Faulet [Mon, 1 Jun 2026 07:45:32 +0000 (09:45 +0200)] 
BUG/MEDIUM: htx: Fix headers rollback on partial copy in htx_xfer()

In htx_xfer() function, when headers are partially copied, depending on the
flags, a rollback may be performed to remove all copied headers from the
destination message. However, there was an issue in the loop performing the
rollback. Instead of decrementing the returned value using the size of the
HTX block from the destination message, the one from the source message was
used. So the wrong value was be returned and in worst case, it could
overflow.

In addition, the BUG_ON() in the loop was removed because test condition was
wrong.

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

38 hours agoBUG/MEDIUM: mux-h1: Dup connection/upgrade value to parse it when making headers
Christopher Faulet [Fri, 29 May 2026 14:26:29 +0000 (16:26 +0200)] 
BUG/MEDIUM: mux-h1: Dup connection/upgrade value to parse it when making headers

When message headers are formatted, the connection and upgrade header values
are parsed to be sanitized and to fill H1M flags. The values are modified in
place without changing the HTX message information accordingly (the block
info and the HTX info). It could be an issue if the output buffer is full
and the header cannot be formatted. Because the formatting can be stopped
with a HTX message in hazardous state.

It should be quite difficult to trigger this issue. But now, a copy of the
value is performed before parsing it. So only the copy will be altered,
leaving the HTX message in a safe state.

This patch must be backported to all stable versions.

38 hours agoBUG/MINOR: cache: Fix copy of value when parsing maxage
Christopher Faulet [Fri, 29 May 2026 14:20:34 +0000 (16:20 +0200)] 
BUG/MINOR: cache: Fix copy of value when parsing maxage

During maxage parsing, the size of the value was not properly computed when
it was copied into the trash chunk. The name (max-age or s-maxage) must be
skipped with the '=' character. But instead of doing a subtraction, and
addition was performed, adding 2 extra bytes to the value used for the
convertion to integer.

In addition, the "chunk_memcat(chk, "", 1)" operation to add a trailing
NULL-byte was replaced by "*(b_tail(chk)) = '\0'". It a bit easier to
understand.

This patch should be backported to all stable versions.

38 hours agoBUG/MEDIUM: h3: fix MAX_PUSH_ID handling
Amaury Denoyelle [Mon, 1 Jun 2026 07:47:45 +0000 (09:47 +0200)] 
BUG/MEDIUM: h3: fix MAX_PUSH_ID handling

MAX_PUSH_ID frames are emitted by the client only on the control stream.
These conditions are checked via h3_check_frame_valid() since the
following patch.

  e4a5a64198bb084eaef2e71bfde65704a5db3931
  BUG/MINOR: h3: reject server MAX_PUSH_ID frame

However control stream test was inverted by mistake. This patch fixes
it.

Due to this bug, H3 connections were improperly closed on error by
haproxy for clients which send MAX_PUSH_ID frames. This has been
detected on the QUIC interop with aioquic and neqo clients.

This must be backported up to 3.3.

38 hours agoCLEANUP: fix comment typo
Amaury Denoyelle [Mon, 1 Jun 2026 07:46:16 +0000 (09:46 +0200)] 
CLEANUP: fix comment typo

Fix comment for H3_UNI_S_T_CTRL used for unidirectional streams.

39 hours agoBUG/MEDIUM: qmux: Close connection on invalid frame
Olivier Houchard [Thu, 28 May 2026 20:01:01 +0000 (22:01 +0200)] 
BUG/MEDIUM: qmux: Close connection on invalid frame

In qcc_qmux_recv(), when calling qmux_parse_frm(), also treat negative
values as an error, and close the connexion. qmux_parse_frm() will
return -1 if the frame is of an invalid type, and we don't want to
process any further, or we will crash.

47 hours agoDOC: security: also add a note to exclude dev/ and admin/
Willy Tarreau [Sun, 31 May 2026 22:46:21 +0000 (00:46 +0200)] 
DOC: security: also add a note to exclude dev/ and admin/

These ones are not intended for production so they're out of scope.
This also fixes a paragraph formatting issue left after a fix.

2 days agoDOC: add security.txt describing how to report security issues
Willy Tarreau [Sun, 31 May 2026 20:42:53 +0000 (22:42 +0200)] 
DOC: add security.txt describing how to report security issues

Move the security contact out of intro.txt into a dedicated, easily
searchable doc/security.txt that points reporters at the threat model
first, and reference it from intro.txt's contacts section and the
documentation index.

2 days agoDOC: internals: add a threat model definition
Willy Tarreau [Sun, 31 May 2026 18:28:08 +0000 (20:28 +0200)] 
DOC: internals: add a threat model definition

Add doc/internals/threat-model.txt describing what does and does not
qualify as a security vulnerability in HAProxy so that reporters and
developers have a common understanding of the threat model, and make it
clear that anything non-critical should be handled in the open and
not hidden behind embargoes.

The document lists assets to protect, what constitutes an attack, what
are the mitigations in place, and the severity ordering of various
risks. This may in the long term also help developers make better
choices of default settings and option names, and may also justify
changing default settings over time when modern operating systems
bring new possibilities.

A section also lists some invariants and defaults in an attempt to
limit the risk of reporting theoretical issues that are technically
impossible to happen in the field.

This is an initial version meant to be refined as cases arise. It
was incrementally designed and cross-checked with the help of three
independent LLMs (Qwen, Gemini and Claude) until each correctly
classified a set of sample reports against it. In the current state
they do not raise any residual ambiguities anymore.

2 days agoDOC: internals: clarify ambiguous wording in core-principles
Willy Tarreau [Sat, 30 May 2026 09:43:43 +0000 (09:43 +0000)] 
DOC: internals: clarify ambiguous wording in core-principles

After testing against a few LLMs, it appeared that several entries in
the core principles document were ambiguous or imprecise and could be
misread (size_t, pools, trash, dwcas, comparison, ncbuf). No more
complaint after this rewording so this will be sufficient for now.

2 days agoCLEANUP: admin/halog: improve handling of memory allocation errors
Ilia Shipitsin [Sun, 31 May 2026 07:19:21 +0000 (09:19 +0200)] 
CLEANUP: admin/halog: improve handling of memory allocation errors

Found via cppcheck  --force --enable=all --output-file=haproxy.log :

admin/halog/halog.c:1805:2: warning: If memory allocation fails, then there is a possible null pointer dereference: ustat [nullPointerOutOfMemory]
admin/halog/halog.c:1806:2: warning: If memory allocation fails, then there is a possible null pointer dereference: ustat [nullPointerOutOfMemory]
admin/halog/halog.c:1809:2: warning: If memory allocation fails, then there is a possible null pointer dereference: ustat [nullPointerOutOfMemory]
admin/halog/halog.c:1810:2: warning: If memory allocation fails, then there is a possible null pointer dereference: ustat [nullPointerOutOfMemory]
admin/halog/halog.c:1814:2: warning: If memory allocation fails, then there is a possible null pointer dereference: ustat [nullPointerOutOfMemory]

2 days agoCLEANUP: ncbmbuf: improve handling of memory allocation errors in unit tests
Ilia Shipitsin [Sun, 31 May 2026 07:14:39 +0000 (09:14 +0200)] 
CLEANUP: ncbmbuf: improve handling of memory allocation errors in unit tests

Found via cppcheck  --force --enable=all --output-file=haproxy.log :

src/ncbmbuf.c:192:9: warning: If memory allocation fails, then there is a possible null pointer dereference: area [nullPointerOutOfMemory]
src/ncbmbuf.c:373:9: warning: If memory allocation fails, then there is a possible null pointer dereference: data [nullPointerOutOfMemory]
src/ncbmbuf.c:546:9: warning: If memory allocation fails, then there is a possible null pointer dereference: data [nullPointerOutOfMemory]

2 days agoMINOR: addons/51degrees: handle memory allocation failures
Ilia Shipitsin [Wed, 13 May 2026 16:12:02 +0000 (18:12 +0200)] 
MINOR: addons/51degrees: handle memory allocation failures

Found via cppcheck  --force --enable=all --output-file=haproxy.log :
addons/51degrees/51d.c:130:3: warning: If memory allocation fails, then
  there is a possible null pointer dereference: name [nullPointerOutOfMemory]
addons/51degrees/51d.c:922:4: warning: If memory allocation fails, then
   there is a possible null pointer dereference: _51d_property_list [nullPointerOutOfMemory]

2 days agoCLEANUP: addons/51degrees: initialize variables
Ilia Shipitsin [Wed, 13 May 2026 16:12:01 +0000 (18:12 +0200)] 
CLEANUP: addons/51degrees: initialize variables

Found via cppcheck  --force --enable=all --output-file=haproxy.log :
addons/51degrees/51d.c:1073:8: error: Uninitialized variable: _51d_prop_name.name [uninitvar]

4 days agoBUG/MEDIUM: resolvers: Wait a bit before calling the xprt prepare_srv
Olivier Houchard [Fri, 29 May 2026 17:38:03 +0000 (19:38 +0200)] 
BUG/MEDIUM: resolvers: Wait a bit before calling the xprt prepare_srv

We can't call call the prepare_srv() method too early, because it needs
global.nbthreads to be properly set, which won't be true at post_parse
time. So instead, make it so that code runs later, as a post_check
function, when it will be safe to do so.

This should be backported up to 2.8.
This should fix github issue #3402

4 days agoBUG/MINOR: cache: fix cache tree iteration
Maxime Henrion [Fri, 29 May 2026 15:55:39 +0000 (11:55 -0400)] 
BUG/MINOR: cache: fix cache tree iteration

Ever since the introduction of multiple cache trees, the "show cache"
CLI command was not properly showing the contents of each tree, but was
only showing the first one.
Fix that by properly resetting next_key when we switch to the next tree.

Should be backported up to 3.0.

4 days agoBUILD: Makefile: put EXTRA_MAKE help at the right place
William Lallemand [Fri, 29 May 2026 14:38:41 +0000 (16:38 +0200)] 
BUILD: Makefile: put EXTRA_MAKE help at the right place

the EXTRA_MAKE help was in the USE_* list which is not the right place
for it, this patch move it to the list of variables in make help

4 days agoMINOR: quic: Copy sin6_flowinfo and sin6_scope_id too
Olivier Houchard [Fri, 29 May 2026 14:17:36 +0000 (16:17 +0200)] 
MINOR: quic: Copy sin6_flowinfo and sin6_scope_id too

In in46un_to_addr(), when copying a struct sockaddr_in6, copy the
sin6_flowinfo and sin6_scope_id, as they are part of the structure too.
They are unlikely to be of any use for us, but this is more correct
anyway.

4 days agoBUG/MINOR: quic: Fix another buffer overflow with sockaddr_in46
Olivier Houchard [Fri, 29 May 2026 14:03:26 +0000 (16:03 +0200)] 
BUG/MINOR: quic: Fix another buffer overflow with sockaddr_in46

Very similarly to what was fixed with commit
63f853957af3ee062493bb3700f964ce456125b0, we cast a sockaddr_in46 in
quic_dgram_parse() to sockaddr_storage while providing source and
destination addresses to qc_handle_conn_migration(), which will then
copy the whole sockaddr_storage, thus reading memory past what was
provided.
While this most likely won't have any impact, let's do the right thing,
and use in46un_to_addr() to generate a real sockaddr_storage.
This does not need to be backported.

4 days agoBUILD: makefile: include EXTRA_MAKE in the .build_opts construction
Willy Tarreau [Fri, 29 May 2026 09:07:38 +0000 (11:07 +0200)] 
BUILD: makefile: include EXTRA_MAKE in the .build_opts construction

EXTRA_MAKE allows to source an external makefile to bring new options
that will result in including add-ons etc. It must be part of the
construction of .build_opts that decides whether or not existing .o
are reusable or need to be rebuilt, otherwise we can end up with a mix
of .o built with some options and others with different options.

No backport is needed, as this appeared in 3.4.

5 days agoMINOR: thread: report when thread-groups or nbthread results in less threads
Willy Tarreau [Wed, 27 May 2026 09:07:09 +0000 (11:07 +0200)] 
MINOR: thread: report when thread-groups or nbthread results in less threads

Some setups where the number of threads is forced without any binding
(no cpu-map), are quite suspicious if they result in less threads than
available CPUs, and not even predictably bound, so we want to notify
the user that this might be an oversight.

Similarly, when thread-groups is forced and not nbthread (and no cpu-map),
and the final number of threads is lower than the hard-limit or the number
of CPUs we also indicate the impact and how to remedy it. This can happen
for example when starting on a machine with more than 64 CPUs and
thread-groups forced to 1, or on more than 128 CPUs and thread-groups
forced to 2 (e.g. when moving an older config to a new platform).

It is possible that some of these conditions might need to be readjusted
in the future to catch other traps or to relax certain commonly used,
valid cases, so for now it is preferable not to backport this patch.

5 days agoMINOR: cpu-topo: notify when cpu-policy is ignored due to other settings
Willy Tarreau [Thu, 28 May 2026 15:41:18 +0000 (17:41 +0200)] 
MINOR: cpu-topo: notify when cpu-policy is ignored due to other settings

The cpu-policy directive is ignored when nbthreads, thread-groups, or
cpu-map are set. In addition, first-usable-node is ignored when the
process was externally restricted (e.g. taskset). This is difficult to
debug when it happens because multiple parameters come into the mix and
it's easy to forget to unset one. Let's emit a notice when this happens
and the policy was forced. This way, it remains silent with the default
policy, but if it was forced, the incompatibility is reported.

It's worth noting that ll the cpu-policy functions take a char **err
but none uses it. It could have been useful here instead of calling
ha_notice() all along, but one needs to determine who the consumers
are and who will be responsible for freeing the message, so let's go
with ha_notice() given that were were already some diag_warnings in
these functions.

It could be helpful to backport this to 3.2.

5 days agoCLEANUP: thread: indicate when max-threads-per-group is ignored
Willy Tarreau [Thu, 28 May 2026 15:19:44 +0000 (17:19 +0200)] 
CLEANUP: thread: indicate when max-threads-per-group is ignored

Since it's easy to get caught by some parameters being ignored, let's
detect when mtpg was explicitly set and report a notice if it is ignored
due to thread-groups being set. For this we need to avoid presetting
the value in the global section and only set it when entering function
thread_detect_count(), which is OK since the value cannot be used before.

5 days agoBUG/MEDIUM: threads: ignore max-threads-per-group when thread-groups is set
Willy Tarreau [Thu, 28 May 2026 14:48:51 +0000 (16:48 +0200)] 
BUG/MEDIUM: threads: ignore max-threads-per-group when thread-groups is set

As documented, max-threads-per-group is the default number of threads
to arrange in a group before creating another group, and is only meant
to be used when thread-groups is not set.

However it was always enforced, so configs like:

   global
       thready-groups 2

which were sufficient in 3.2 and above to start with 64-128 threads
are now suddenly limited to 32 threads! Let's relax the limit when
thread-groups is set!

No backport is needed since this is only 3.4.

5 days agoBUG/MINOR: threads: set at least grp_max when mtpg is too small
Willy Tarreau [Thu, 28 May 2026 15:15:57 +0000 (17:15 +0200)] 
BUG/MINOR: threads: set at least grp_max when mtpg is too small

When starting, say, 128 threads with max-threads-per-group set to 2
and MAX_TGROUPS set to the default 32, instead of setting the resulting
number of groups to 32 and threads to 64, they're set to 1 and 32
respectively because the condition to raise grp_min is not satisfied.

Let's cut the condition in two parts to also permit to raise it at
least to grp_max.

This should be backported to 3.2.

5 days agoREGTESTS: quic: disable quic/ocsp_auto_update for now
Willy Tarreau [Thu, 28 May 2026 16:44:36 +0000 (18:44 +0200)] 
REGTESTS: quic: disable quic/ocsp_auto_update for now

It was made from the split of the original one into the SSL and the QUIC
variant. However there's a catch: both use the same certificates which
includes the OCSP URL 127.0.0.1:12345, and both need to start a server
on that port. Depending on the number of parallel process and their
speed, they might very well work, or totally fail due to a binding
conflict and the fact that the test runs for a few seconds.

Let's disable the QUIC variant for now, since the whole point of the
test is to verify all the sequencing, the SSL one is greatly sufficient.
Maybe a better approach can be found later.

5 days agoBUG/MEDIUM: lua: register hlua_init() as a pre-check to fix crash without Lua config
William Lallemand [Thu, 28 May 2026 16:27:04 +0000 (18:27 +0200)] 
BUG/MEDIUM: lua: register hlua_init() as a pre-check to fix crash without Lua config

Commit 1c59c39171529 deferred hlua_init() to be called lazily from the
config keyword handlers (lua-load, lua-load-per-thread,
lua-prepend-path, tune.lua.openlibs), with a call inside
hlua_post_init() as a safety net for the case where no Lua directive
appears in the configuration at all.

The problem is hlua_init() is a function that allocates internal
servers (socket_proxy, socket_tcp, socket_ssl) that must exist before
haproxy initialize the configuration. But hlua_post_init() is done too
far after this initialization, so the safety net does not work
correctly.

This would results in a crash in the deinit() if no lua
configuration was loaded in haproxy.

   Core was generated by `./haproxy -W -f /dev/null'.
   Program terminated with signal SIGSEGV, Segmentation fault.
   #0  0x00005671c72b1047 in _ceb_first (root=0x30, kofs=16, key_type=CEB_KT_U64, key_len=0,
       is_dup_ptr=0x7ffc13197a14) at include/import/cebtree-prv.h:1160
   1160 if (!*root)
   (gdb) bt
   #0  0x00005671c72b1047 in _ceb_first (root=0x30, kofs=16, key_type=CEB_KT_U64, key_len=0,
       is_dup_ptr=0x7ffc13197a14) at include/import/cebtree-prv.h:1160
   #1  _ceb64_first (root=0x30, kofs=16) at src/_ceb_int.c:73
   #2  ceb64_ofs_first (root=0x30, kofs=16) at src/_ceb_int.c:66
   #3  0x00005671c6be5e6e in srv_close_idle_conns (srv=0x5671fd592a80) at src/server.c:7676
   #4  0x00005671c6d3be17 in deinit_proxy (p=0x5671fd5d7780) at src/proxy.c:393
   #5  0x00005671c6d3c536 in proxy_drop (p=0x5671fd5d7780) at src/proxy.c:479
   #6  0x00005671c6aed998 in hlua_deinit () at src/hlua.c:14934
   #7  0x00005671c6db2e41 in deinit () at src/haproxy.c:2846
   #8  0x00005671c6db3d98 in deinit_and_exit (status=0) at src/haproxy.c:2966
   #9  0x00005671c6db6111 in main (argc=4, argv=0x7ffc131983c8) at src/haproxy.c:3997

The fix is to do the initialization earlier, in a pre-check callback.

Thanks to Amaury for reporting this issue.

No backport needed.

5 days agoRevert "MEDIUM: quic: optimize HKDF operations by reusing per-thread contexts"
Frederic Lecaille [Thu, 28 May 2026 16:15:19 +0000 (18:15 +0200)] 
Revert "MEDIUM: quic: optimize HKDF operations by reusing per-thread contexts"

This reverts commit 4e0af590e8384ec5ede80ae725d25bc7178927f2.
This patch does not work at all with AWSLC! This is incredible!

No need to backport.

5 days agoBUG/MINOR: quic: update drs->lost before calling on_ack_recv
Frederic Lecaille [Thu, 28 May 2026 15:41:30 +0000 (17:41 +0200)] 
BUG/MINOR: quic: update drs->lost before calling on_ack_recv

The QUIC congestion control algorithm impacted by this bug is BBR.

In qc_notify_cc_of_newly_acked_pkts(), drs->lost was updated after
quic_cc_drs_on_ack_recv(), causing the current sample's lost count to
miss the bytes_lost from the current loss detection round. This meant
that rs->lost = drs->lost - rs->prior_lost would always be 0 for the
current losses, since both drs->lost and rs->prior_lost (captured at
packet send time) excluded the current bytes_lost.

Moving drs->lost += bytes_lost before on_ack_recv ensures that the
rate sample correctly includes the newly detected lost bytes, matching
the BBR algorithm's intent where C.delta_lost = C.lost - C.prior_lost
should reflect all losses since the last sample.

Must be backported as far as 3.1 where delivery rate sampling was
implemented.

5 days agoBUG/MEDIUM: quic: reset consecutive_losses on exit from recovery period (cubic)
Frederic Lecaille [Thu, 28 May 2026 13:12:11 +0000 (15:12 +0200)] 
BUG/MEDIUM: quic: reset consecutive_losses on exit from recovery period (cubic)

When exiting the recovery period and re-entering congestion avoidance,
the consecutive_losses counter was not reset. This meant that if a loss
event arrived immediately after the ACK that ended recovery, the counter
would still hold the value that triggered recovery, causing an immediate
re-entry into recovery (recovery -> CA -> recovery loop).

Resetting consecutive_losses to 0 on recovery exit matches the behavior
of resetting it on ACK in CA, ensuring a clean slate for the new
congestion avoidance period.

Must be backported to all versions.

5 days agoBUG/MEDIUM: quic: reset cwnd in slow_start on persistent congestion (cubic)
Frederic Lecaille [Thu, 28 May 2026 13:08:07 +0000 (15:08 +0200)] 
BUG/MEDIUM: quic: reset cwnd in slow_start on persistent congestion (cubic)

The cubic slow_start callback was only resetting the internal cubic state
without reducing the congestion window, unlike newreno which calls
quic_cc_path_reset(). Per RFC 9002, persistent congestion should trigger
both entry into slow start and a reduction of the congestion window.

Must be backported to all versions.

5 days agoMEDIUM: quic: optimize HKDF operations by reusing per-thread contexts
Frederic Lecaille [Thu, 28 May 2026 12:49:07 +0000 (14:49 +0200)] 
MEDIUM: quic: optimize HKDF operations by reusing per-thread contexts

Allocating and freeing an OpenSSL EVP_PKEY_CTX context via
EVP_PKEY_CTX_new_id() and EVP_PKEY_CTX_free() on every HKDF cryptographic
operation (such as during stateless reset token generation) induces
unnecessary memory allocation overhead.

Optimize this by introducing a global per-thread context array
'quic_tls_hkdf_ctxs'. These contexts are allocated and initialized once
at startup via a POST_CHECK hook (quic_tls_alloc_hkdf_ctxs) and are
properly freed at exit via a POST_DEINIT hook (quic_tls_dealloc_hkdf_ctxs).

The functions quic_hkdf_extract(), quic_hkdf_expand(), and
quic_hkdf_extract_and_expand() now reuse the pre-allocated context
corresponding to the current thread ID ('tid'), removing dynamic
allocations from these frequent execution paths.

As a cleanup, quic_hkdf_expand() is now static and unexported from the
header file.

Should be easily backported to all versions for optimization purposes.

5 days agoBUG/MINOR: quic: fix ack range node pool_free call passing wrong pointer type
Frederic Lecaille [Thu, 28 May 2026 05:54:32 +0000 (07:54 +0200)] 
BUG/MINOR: quic: fix ack range node pool_free call passing wrong pointer type

In quic_insert_new_range(), the variable 'first' is a struct eb64_node*,
but pool_free expects a struct quic_arng_node*. While the addresses are identical
(since 'first' is the first member of quic_arng_node), this is technically
incorrect and should use eb64_entry() for proper type safety.

Must be backported to all versions.

5 days agoBUG/MINOR: mux_quic: prevent BE reuse with an errored conn
Amaury Denoyelle [Thu, 28 May 2026 13:55:56 +0000 (15:55 +0200)] 
BUG/MINOR: mux_quic: prevent BE reuse with an errored conn

When a backend connection is reused, qcm_strm_attach() callback is used.
A BUG_ON() is present to ensure that the connection is not already on
error. This should be guaranteed by the fact that idle insertion is
skipped for such connections.

However, when a connection is flagged on error, it is not immediately
removed from its idle/avail pool. Thus, there is a risk that it is
reused, triggering the aformentioned BUG_ON() statement.

This issue should be avoided via avail_streams callback which should
return 0, forcing the caller to cancel the connection reuse. In QUIC,
this callback implementation relies on internal qcc_be_is_reusable().
However, it lacked checks for error status.

To fix this, extend qcc_be_is_reusable() to properly check connection
errors or an expired timeout.

Previously, these parameters were already checked by qcc_is_dead(). As
it also relies on qcc_be_is_reusable(), this patch also rearranges it to
avoid duplicate checks for backend connections.

This should be backported up to 3.3.

5 days agoBUG/MINOR: mux_quic: fix BE conn removal on app shutdown
Amaury Denoyelle [Thu, 28 May 2026 14:44:03 +0000 (16:44 +0200)] 
BUG/MINOR: mux_quic: fix BE conn removal on app shutdown

When QUIC application layer is shut for a backend connection, the
connection is immediately removed from its idle pool. This is a nice
optimization as this prevents a future streams to try to reuse an
unusable connection. This is implemented since the following commit.

  00d668549e46b34d29ea3daa1f6dd42b5251a365
  MINOR: mux-quic: do not reuse connection if app already shut

However, this removal is not correctly performed as it is used
conn_delete_from_tree(). For private connections, this can cause crashes
as they are stored in the session instead. Thus, connection status is
now properly check, and alternatively session_unown_conn() is used if
stored in the session.

This must be backported up to 3.3.

5 days agoBUG/MINOR: mux_quic: open an idle QCS on reset on BE side
Amaury Denoyelle [Thu, 23 Apr 2026 12:23:01 +0000 (14:23 +0200)] 
BUG/MINOR: mux_quic: open an idle QCS on reset on BE side

On the backend side, a QCS may be opened but resetted immediately. No
STREAM frame will be emitted prior to the RESET_STREAM. When the latter
is sent, qcs_close_local() will mark the QCS Tx channel as closed.

In this case, a BUG_ON() would be triggered as there is QCS Tx channel
is not yet marked as opened. To prevent this, add a qcs_idle_open() call
when the stream is resetted, but only for the backend side.

This should be backported up to 3.3.

5 days agoMINOR: mux_quic/flags: add missing flags
Amaury Denoyelle [Thu, 28 May 2026 13:49:21 +0000 (15:49 +0200)] 
MINOR: mux_quic/flags: add missing flags

Add missing mux QUIC values for the dev flags utility, both for qcc and
qcs types.

5 days agoBUILD: addons: convert WURFL addon to EXTRA_MAKE 20260528-extra-make flx04/20260528-extra-make
William Lallemand [Thu, 28 May 2026 14:33:30 +0000 (16:33 +0200)] 
BUILD: addons: convert WURFL addon to EXTRA_MAKE

Move the WURFL Makefile part to addons/wurfl/Makefile.mk so it can be
used with EXTRA_MAKE and allow to cleanup the main Makefile.

Shouldn't have impact on the build system, every build variable
previously used are the same.

5 days agoBUILD: addons: convert deviceatlas addon to EXTRA_MAKE
William Lallemand [Thu, 28 May 2026 14:25:44 +0000 (16:25 +0200)] 
BUILD: addons: convert deviceatlas addon to EXTRA_MAKE

Move the deviceatlas Makefile.inc to Makefile.mk so it can be used with
EXTRA_MAKE and allow to cleanup the main Makefile.

EXTRA_MAKE paths are appended with /Makefile.mk via addsuffix, so the
path must not have a trailing slash.

Shouldn't have impact on the build system, every build variable
previously used are the same.

5 days agoBUILD: addons: convert 51d addon to EXTRA_MAKE
William Lallemand [Thu, 28 May 2026 14:17:57 +0000 (16:17 +0200)] 
BUILD: addons: convert 51d addon to EXTRA_MAKE

Move the 51degrees Makefile part to addons/51degrees/Makefile.mk so it
can be used with EXTRA_MAKE and allow to cleanup the main Makefile.

EXTRA_MAKE paths are appended with /Makefile.mk via addsuffix, so the
path must not have a trailing slash.

Shouldn't have impact on the build system, every build variable
previously used are the same.

5 days agoBUG/MINOR: mux-h2: Count padding for connection flow control on error path
Christopher Faulet [Thu, 28 May 2026 12:42:16 +0000 (14:42 +0200)] 
BUG/MINOR: mux-h2: Count padding for connection flow control on error path

When DATA frame are received, we take care to update the counter used to
send WINDOW_UPDATE for the connection. It is also performed on error path
when DATA frames are processed. However, when this happened, only the frame
length was accounted while the padding must also be considered.

To fix the issue, the full frame length (h2c->dfl), which include the
padding length, must be added to the amount of newly received data
(h2c->rcvd_c).

The issue was introduced with commit eeacca75d ("BUG/MINOR: mux-h2: count
rejected DATA frames against the connection's flow control") and backported
to 2.8.

So this patch must be backported as far as 2.8.

5 days agoREGTESTS: lua: fix tune.lua.openlibs in Lua reg-tests
William Lallemand [Wed, 27 May 2026 19:06:04 +0000 (21:06 +0200)] 
REGTESTS: lua: fix tune.lua.openlibs in Lua reg-tests

These tests were using "tune.lua.openlibs none" with lua-load, which
was a no-op in the old code since Lua states 0 and 1 were always
initialised before config parsing with all standard libraries.

Now that the Lua VM is initialised lazily, the restriction correctly
applies to state 0 as well. Replace "none" with the minimal set of
libraries actually required by each test's Lua code:

  - lua_socket.vtc, h_txn_get_priv.vtc, lua_httpclient.vtc: string
  - txn_get_priv.vtc: string,table

5 days agoBUG/MEDIUM: lua: defer Lua VM initialisation to the first Lua config keyword
William Lallemand [Wed, 27 May 2026 17:54:48 +0000 (19:54 +0200)] 
BUG/MEDIUM: lua: defer Lua VM initialisation to the first Lua config keyword

HAProxy used to call hlua_init() unconditionally from step_init_1(),
before any configuration file was parsed.  As a consequence, Lua states
0 and 1 were always created with hlua_openlibs_flags set to its default
value (HLUA_OPENLIBS_ALL), regardless of any tune.lua.openlibs directive
that appeared later in the global section.  With multiple threads, states
2..N were created correctly in hlua_post_init() after the config had been
parsed, while states 0 and 1 retained the full standard-library set.
This produced the observable bug reported in GitHub issue #3396: a script
loaded with lua-load-per-thread could see require() as a function on
thread 1 but nil on thread 2 when tune.lua.openlibs was used to restrict
the available libraries.

The initialisation is now lazy.  hlua_init() is idempotent: it returns
immediately if the states already exist (hlua_states[0] != NULL).  It is
called explicitly from the three config keyword handlers that need the
Lua states to be live before they can do their work (lua-load,
lua-load-per-thread, lua-prepend-path) and from tune.lua.openlibs, after
the hlua_openlibs_flags variable has been updated, so that the states are
always created with the correct library set.

hlua_post_init() calls hlua_init() unconditionally as a safety net,
covering the case where no Lua directive appeared in the configuration at
all (no global section, or only pure-tuning directives such as timeouts
and memory limits), and ensuring correct behaviour with multiple
consecutive global sections.

As a result of this change, tune.lua.openlibs must now appear before
lua-load, lua-load-per-thread, and lua-prepend-path in the configuration;
if any of those keywords is encountered first, the Lua states will already
be initialised and tune.lua.openlibs with a non-default value will return
a parse error.

No backport needed.

5 days agoBUG/MINOR: quic: Fix memory leak in quic_deallocate_dghdlrs()
Frederic Lecaille [Tue, 19 May 2026 15:06:08 +0000 (17:06 +0200)] 
BUG/MINOR: quic: Fix memory leak in quic_deallocate_dghdlrs()

When deallocating the QUIC datagram handlers, the per-thread buffer
allocated inside quic_dghdlrs[i].buf.buffer was missing a free().
This led to a memory leak on exit or reload.

Fix this by freeing each thread buffer before releasing the main
quic_dghdlrs array.

5 days agoBUG/MEDIUM: quic: handle ECONNREFUSED on RX side
Frederic Lecaille [Fri, 24 Apr 2026 09:01:37 +0000 (11:01 +0200)] 
BUG/MEDIUM: quic: handle ECONNREFUSED on RX side

Unlike the detection performed during sendto() for an unreachable peer,
ECONNREFUSED was not handled when received via recvmsg() as an ICMP
"host unreachable" message.

This patch tracks ECONNREFUSED errors on the receive path.

Note that this detection is entirely dependent on the remote host effectively
sending an ICMP "host unreachable" message and on the absence of any network
filtering (e.g., firewalls) that would drop such ICMP packets. Without
receiving this ICMP signal, the connection state cannot be updated through
this mechanism.

At a higher level, similar to how this error is handled on sendto(),
the connection is now terminated as soon as possible by calling
qc_kill_conn(). This triggers a call to qc_notify_err(). When the mux
does not exist, it attempts to create one via conn_create_mux(). While
the latter systematically fails if the connection is flagged with
CO_FL_ERROR, it has the useful side effect of waking the stconn stream
attached to the connection during a session opening without a mux
(e.g., for H3).

This issue was caught by haload (upcoming tool).

Must be backported as far as 2.6 because it impacts both the QUIC
frontends and backends.

6 days agoCLEANUP: qpack: move encoded macros to qpack-t.h to avoid duplication 20260527-fle-ia-review flx04/20260527-fle-ia-review
Frederic Lecaille [Wed, 27 May 2026 16:38:32 +0000 (18:38 +0200)] 
CLEANUP: qpack: move encoded macros to qpack-t.h to avoid duplication

QPACK_LFL_WLN_BIT and related encoded field line bitmasks were defined
in both qpack-enc.c and qpack-dec.c. Moved them to qpack-t.h where
they are shared between encoder and decoder, eliminating the duplicate
definitions.

Should be backported to ease any further commit to come.

6 days agoBUG/MINOR: qpack: fix huff_dec() error handling in qpack_decode_fs()
Frederic Lecaille [Wed, 27 May 2026 15:16:16 +0000 (17:16 +0200)] 
BUG/MINOR: qpack: fix huff_dec() error handling in qpack_decode_fs()

The <nlen> variable is a signed integer, but the check for a Huffman
decoding error was written as 'nlen == (uint32_t)-1'.

With standard compiler type promotion rules, this comparison happens to
work as intended when huff_dec() returns -1. However, relying on implicit
unsigned promotions for signed error checking is fragile. If a compiler
applies different promotion semantics, or if huff_dec() returns any other
negative error code, the failure would go undetected, leading to buffer
corruption or a crash via b_add() and ist2().

Fix this by using 'nlen < 0', removing any ambiguity regardless of the
compiler used.

Must be backported to all versions.

6 days agoCLEANUP: qpack: fix copy-paste typo in value Huffman debug string for WLN
Frederic Lecaille [Wed, 27 May 2026 15:07:26 +0000 (17:07 +0200)] 
CLEANUP: qpack: fix copy-paste typo in value Huffman debug string for WLN

In qpack_decode_fs(), inside the QPACK_LFL_WLN_BIT branch (Literal field
line with literal name), the debug message printed "[name huff ...]" instead
of "[value huff ...]" after decoding the value string.

This is a harmless copy-paste typo from the preceding name decoding block.

Even if this is a cleanup, should be easily backported to ease any further
backport.

6 days agoBUG/MINOR: qpack: fix sign bit mask in qpack_decode_fs_pfx()
Frederic Lecaille [Wed, 27 May 2026 14:40:52 +0000 (16:40 +0200)] 
BUG/MINOR: qpack: fix sign bit mask in qpack_decode_fs_pfx()

The sign bit of the Delta Base integer encoding was extracted using
mask 0x8 (bit 3) instead of 0x80 (bit 7). This was likely a copy-paste
error from other QPACK instructions using 3-bit varints.

According to RFC 9204 Section 5.2.1, for prefix instructions, the sign
bit 'S' is the most significant bit (bit 7) of the first byte, followed
by a 7-bit varint.

This fix is harmless for current HTTP/3 traffic: per RFC 9204, the Delta
Base calculation is strictly used for dynamic table entry references.
Since HAProxy's QPACK dynamic table is currently disabled and the extracted
sign bit is not yet used in the decoding logic (only in debug prints),
this code path has no impact on production for now.

Must be backported to all versions.

6 days agoCLEANUP: qpack: fix copy-paste typo in value Huffman debug string
Frederic Lecaille [Wed, 27 May 2026 13:18:40 +0000 (15:18 +0200)] 
CLEANUP: qpack: fix copy-paste typo in value Huffman debug string

In qpack_decode_fs(), when decoding a literal field line with a literal
value, the debug message mistakenly printed "[name huff ...]" instead of
"[value huff ...]" after a successful Huffman decoding of the value string.

This is a harmless copy-paste typo from the field name decoding block
just above, fix it to prevent confusion when debugging QPACK streams.

Should be easily backported to all versions to ease further modifications
into the QPACK code.

6 days agoBUG/MINOR: qpack: fix potential null-pointer dereference in qpack_dht_insert()
Frederic Lecaille [Wed, 27 May 2026 13:00:30 +0000 (15:00 +0200)] 
BUG/MINOR: qpack: fix potential null-pointer dereference in qpack_dht_insert()

When defragmenting the QPACK dynamic header table upfront during an
insertion, qpack_dht_defrag() can fail and return NULL if memory
allocation or re-allocation fails.

However, qpack_dht_insert() was blindly using the returned pointer
without validation, immediately leading to a null-pointer dereference
on 'dht->wrap'.

Fix this by checking if 'dht' is NULL after the defrag call and return
an error (-1).

Note that this has no impact on production yet because the QPACK dynamic
table is currently not enabled/used, so qpack_dht_insert() is never called.

Should be easily backported to all versions.

6 days agoBUG/MINOR: qpack: Fix index calculation in debug functions
Frederic Lecaille [Tue, 26 May 2026 09:26:23 +0000 (11:26 +0200)] 
BUG/MINOR: qpack: Fix index calculation in debug functions

Although qpack_idx_to_name and qpack_idx_to_value are currently only
called within uncompiled debug code, they contained an index bug. They
passed absolute indexes directly to qpack_get_dte instead of relative
dynamic table indexes.

This patch fixes the logic by subtracting QPACK_SHT_SIZE and guarding
against static table index lookups.

Should be easily backported to all versions.

6 days agoRevert "BUG/MEDIUM: dns: fix long loops in additional records parse on name failure"
Christopher Faulet [Wed, 27 May 2026 13:37:35 +0000 (15:37 +0200)] 
Revert "BUG/MEDIUM: dns: fix long loops in additional records parse on name failure"

This reverts commit fefce297ab5d0c36d6d6773092c976ea6166dc1e.

The commit broke the resolvers. All responses are marked as invalid. The
resolv_read_name() function can return 0 on error, but it seems also
possible to return 0 when no label name was found. And depending on the
caller, it can be an error... or not.

So, let's revert it. This might trigger a watchdog but doesn't seem to and
once fixed it makes things worse.

Must be backported as far as 2.4.

6 days agoBUG/MINOR: qmux: reject too large initial record
Amaury Denoyelle [Wed, 27 May 2026 13:34:01 +0000 (15:34 +0200)] 
BUG/MINOR: qmux: reject too large initial record

Initial max_record_size is set to 16382. If the first received record
size is larger, abort xprt_qmux layer immediately without having to wait
for the timeout.

No need to backport.

6 days agoBUG/MEDIUM: qmux: do not crash on receiving an invalid first frame
Amaury Denoyelle [Wed, 27 May 2026 13:35:34 +0000 (15:35 +0200)] 
BUG/MEDIUM: qmux: do not crash on receiving an invalid first frame

With QMux, each peer has to first emit a transport parameters frame. If
the received frame is different, xprt_qmux handshake cannot proceed.
This patch removes the BUG_ON() in this case, replacing it with a safer
connection closure.

In the future, a graceful close with CONNECTION_CLOSE frame should be
implemented.

No need to backport.

6 days agoBUG/MEDIUM: qmux: do not crash on too large record
Amaury Denoyelle [Wed, 27 May 2026 13:30:04 +0000 (15:30 +0200)] 
BUG/MEDIUM: qmux: do not crash on too large record

Remove BUG_ON() when reading a QMux record larger than the buffer. It is
now replaced by a safer error handling. In the future, a proper
CONNECTION_CLOSE emission should be implemented for this case.

No need to backport.

6 days agoBUG/MEDIUM: cpu-topo: Enforce thread-hard-limit on policy
Olivier Houchard [Wed, 27 May 2026 09:13:52 +0000 (11:13 +0200)] 
BUG/MEDIUM: cpu-topo: Enforce thread-hard-limit on policy

When a policy is set, and the number of threads is calculated
dynamically, make sure we enforce thread-hard-limit, and do not create
thread groups based on how many thread we would have created without
the limit.
This should be backported to 3.3 and 3.2. The patch won't apply cleanly
there, because the code has changed since then, but it should be very
similar, only we'll have to check "cpu_count" there, where in 3.4 we
check "thr_count".

6 days agoBUG/MINOR: mux-h1: H2 preface rejection doesn't update stick-table glitches
Chad Lavoie [Fri, 22 May 2026 17:58:38 +0000 (13:58 -0400)] 
BUG/MINOR: mux-h1: H2 preface rejection doesn't update stick-table glitches

commit 72fd357814e1 ("MEDIUM: mux-h1: Return an error on h2 upgrade
attempts if not allowed") added an h1_report_glitch() call on the new
405 path but exits via "goto no_parsing", which skips the
session_add_glitch_ctr() call at the end of the parse block. As a
result fc_glitches increments correctly but the per-session stick
counters never see it, breaking sc_glitch_cnt-based rate limiting of
the H2-preface-over-H1 abuse pattern.

No backport needed beyond the branches that took 72fd357814.

[cf: Patch was edited to move the goto label instead of duplicating
     the call to session_add_glitch_ctr]

6 days agoBUG/MINOR: ssl-gencert: validate SNI characters to prevent SAN certificate injection
William Lallemand [Mon, 25 May 2026 12:55:18 +0000 (12:55 +0000)] 
BUG/MINOR: ssl-gencert: validate SNI characters to prevent SAN certificate injection

ssl_sock_add_san_ext() builds the Subject Alternative Name extension by
concatenating "DNS:" + servername and passing the result to
X509V3_EXT_nconf_nid(). OpenSSL's nconf parser splits the value string on
commas into multiple type:value SAN entries. The SNI comes from unauthenticated
TLS ClientHello data -- an attacker can embed commas and colons (e.g.,
"host,dns:internal.corp,ip:10.0.0.1") to inject arbitrary GENERAL_NAME entries
into certificates signed by HAProxy's configured CA.

This is a CA issuance-policy violation: the operator expects one certificate
per SNI hostname, but an attacker can obtain certificates containing additional
hostnames/IPs/emails without access to the CA private key.

Fix by adding ssl_sock_sni_is_valid() that validates the SNI contains only
DNS-label-legal characters (alphanumeric, hyphens, dots). The check is
performed at the start of ssl_sock_do_create_cert() before any allocation.
Commas, colons, spaces, and other special characters cause certificate
generation to fail, preventing SAN injection while allowing all valid
hostname values.

Must be backported in every maintained branches.

6 days agoBUG/MINOR: tcpcheck: Check LDAP response to not read more data than available
Christopher Faulet [Wed, 27 May 2026 07:16:37 +0000 (09:16 +0200)] 
BUG/MINOR: tcpcheck: Check LDAP response to not read more data than available

tcpcheck_ldap_expect_bindrsp() parses ASN.1 BER-encoded LDAP responses from
the health check target. After reading the outer message size and validating
protocol fields, it encounters a long-form BER length for the bindResponse
value (high bit set in the length byte). The code reads nbytes = (*ptr &
0x7f) then advances ptr by 1 + nbytes without checking that enough bytes
remain in the receive buffer. So, it is possible to read more data than
available.

Note that it is only possible if the LDAP response was forged because the
message length was already checked. LDAP response remains quite short and it
is not possible to read outside the buffer area. So at worst, garbage are
parsed and a wrong result is reported by the LDAP health-check. Most
probably an error will be reported.

This patch could be backported to all stable versions.

7 days ago[RELEASE] Released version 3.4-dev14 v3.4-dev14
Willy Tarreau [Tue, 26 May 2026 19:56:40 +0000 (21:56 +0200)] 
[RELEASE] Released version 3.4-dev14

Released version 3.4-dev14 with the following main changes :
    - MINOR: config: shm-stats-file is no longer experimental
    - BUILD: proxy: unstatify the proxies_del_lock to avoid a warning without threads
    - BUG/MEDIUM: net_helper: fix a remaining possibly infinite loop in converters
    - MINOR: ssl_sock: remove unneeded check on QMux flags
    - MINOR: connection: define xprt_add_l6hs()
    - MINOR: xprt_qmux: define default value for get_alpn
    - MINOR: connection: define mask CO_FL_WAIT_XPRT_L6
    - MINOR: session: support QMux in clear on FE side
    - MINOR: backend: support QMux in clear for BE side
    - BUG/MINOR: ocsp: Manage date too far away in the future
    - MINOR: mux_quic: handle STOP_SENDING in QMux
    - MINOR: mux_quic: handle MAX_STREAMS for uni stream in QMux
    - MINOR: mux_quic: do not crash on unhandled QMux frame reception
    - BUG/MEDIUM: applet: Properly handle receives of size 0
    - BUG/MEDIUM: resolvers: Fix test on dn label size in resolv_dn_label_to_str()
    - BUG/MEDIUM: ssl-gencert: Unlock LRU cache if failing to generate certificate
    - BUG/MINOR: quic: fix ODCID lookup from derived value
    - BUG/MEDIUM: dict: hold lock while decrementing refcount in dict_entry_unref
    - BUG/MINOR: tcpchecks: Limit parsing of agent-check reply to the buffer
    - BUG/MEDIUM: hlua: Fix integer underflow when receiving line from lua cosocket
    - BUG/MEDIUM: cli: Fix parsing of pattern finishing a command payload
    - BUG/MEDIUM: acme: NUL terminate response buffer before PEM parsing
    - BUILD: intops: mask the fail value in array_size_or_fail()
    - BUG/MEDIUM: log-forward: make sure the month is unsigned
    - BUG/MEDIUM: regex: allocate a large enough pcre2 match for all matches
    - BUG/MEDIUM: tcpcheck/spoe: bound the SPOP error code to valid values
    - BUG/MEDIUM: cache: fix a refcount leak for missed secondary entries
    - BUG/MINOR: log: free logformat expr on compile failure in cfg_parse_log_profile
    - BUG/MINOR: resolvers: fix room for trailing zero in resolv_dn_label_to_str()
    - BUG/MINOR: resolvers: fix risk of appending garbage past the domain name
    - BUG/MINOR: mux-h2: validate HEADERS frame length before reading stream dep
    - BUG/MINOR: log: look for the end of priority before the end of the buffer
    - BUG/MINOR: dict: fix refcount race on insert collision
    - BUG/MINOR: init: use more than ha_random64() for the cluster secret
    - BUG/MINOR: sample: limit the be2hex converter's chunk size
    - CLEANUP: resolvers: use read_n32() instead of open-coded big-endian read
    - CLEANUP: resolvers: remove pool_free(NULL) in SRV additional record matching
    - CLEANUP: resolvers: fix comment typos and wrong filenames in file headers
    - BUG/MINOR: haterm: fix the random suffix multiplication
    - MINOR: haterm: enable h3 for TCP bindings
    - MINOR: haterm: do not emit a warning when not using SSL
    - BUG/MEDIUM: h1: drop headers whose names contain invalid chars
    - BUG/MEDIUM: h1: limit status codes to 3 digits by default
    - BUG/MEDIUM: cache: always verify the primary hash in get_secondary_entry()
    - BUG/MINOR: cache: also recognize directives in the form "token="
    - BUG/MINOR: resolvers: relax size checks in authority record parsing
    - BUG/MINOR: sample: request an extra output byte for the url_dec converter
    - BUG/MINOR: http-fetch: check against the whole token in get_http_auth()
    - BUG/MEDIUM: acme: protect against risk of null-deref on connection failure
    - BUG/MINOR: http-ext: always check remaining data when reading rfc7239 nodeport
    - BUG/MINOR: base64: return empty string for empty input in base64dec()
    - BUG/MINOR: payload: fix the handshake length bounds check smp_client_hello_parse()
    - BUG/MINOR: ssl-hello: make use of the null-terminated servername
    - BUG/MINOR: resolvers: switch to a better PRNG for query IDs
    - BUG/MINOR: addons/51d: NUL-terminate headers before passing them to Trie API
    - BUG/MEDIUM: tools: insert an XXH64 layer on the PRNG output
    - MINOR: tools: provide a function to generate a hashed random pair
    - MEDIUM: init: fall back to ha_random64_pair_hashed() for the cluster secret
    - MEDIUM: tools: use the hashed random pair for UUID generation
    - MEDIUM: h1: use ha_random64_pair_hashed() for the WebSocket key
    - MEDIUM: quic: use ha_random64_pair_hashed() to generate the QUIC retry tokens
    - MEDIUM: tools: switch the main PRNG to a thread-local xoshiro256**
    - BUG/MEDIUM: h3: reject client push stream
    - BUG/MINOR: h3: reject server push stream
    - BUG/MINOR: h3: reject client CANCEL_PUSH frame
    - BUG/MINOR: h3: adjust error on PUSH_PROMISE frame reception
    - BUG/MINOR: h3: reject server MAX_PUSH_ID frame
    - BUG/MEDIUM: auth: fix unconfigured password NULL deref
    - BUG/MINOR: h3: add missing break on rcv_buf()
    - BUG/MINOR: hlua: prevent Lua from passing CR/LF/NUL in HTTP headers
    - BUG/MINOR: qmux: do not crash on frame parsing issue
    - BUG/MINOR: quic: reject packet too short for HP decryption
    - BUG/MINOR: jwe: enforce GCM tag length to 128 bits
    - BUG/MEDIUM: jwe: substitute random CEK on RSA1_5 decryption failure per RFC 7516 #11.5
    - BUG/MEDIUM: mux-fcgi: reject stream ID 0 for application records
    - MINOR: http: Add function to remove all occurrences of a value in a header
    - MINOR: h1: Add  a H1M flag to specify a non-empty 'Upgrade:' header was parsed
    - BUG/MEDIUM: h1-htx: Sanitize parsing to properly handle upgrade requests
    - BUG/MINOR: mux-fcgi: Use relative offset to compute contig data in demux buf
    - BUG/MINOR: mux-spop: Use relative offset to compute contig data in demux buf
    - CLEANUP: mux-fcgi/mux-spop: Remove copy/pasted comment about slow realign

7 days agoCLEANUP: mux-fcgi/mux-spop: Remove copy/pasted comment about slow realign
Christopher Faulet [Tue, 26 May 2026 16:20:01 +0000 (18:20 +0200)] 
CLEANUP: mux-fcgi/mux-spop: Remove copy/pasted comment about slow realign

A comment about the condition to perform a slow realign of the demux buffer
was abusively copy/pasted from the FCGI multiplexer at different places in
the FCGI and SPOP multiplexers. Let's remove these comments.

7 days agoBUG/MINOR: mux-spop: Use relative offset to compute contig data in demux buf
Christopher Faulet [Tue, 26 May 2026 16:15:25 +0000 (18:15 +0200)] 
BUG/MINOR: mux-spop: Use relative offset to compute contig data in demux buf

b_contig_data() should be called with a head-relative offset (0 for the
beginning of readable data). However, in the SPOP multiplexer, to get
contiguous data available in the demux buffer, it is called with
b_head_ofs(dbuf) which returns an absolute buffer position (b->head). So
b->head is counted twice. Because of this bug, the demux buffer could be
realigned while it should not and conversely.

Instead, the offset 0 must be used. So let's fix it.

This patch must be backported as far as 3.2.

7 days agoBUG/MINOR: mux-fcgi: Use relative offset to compute contig data in demux buf
Christopher Faulet [Tue, 26 May 2026 14:31:32 +0000 (16:31 +0200)] 
BUG/MINOR: mux-fcgi: Use relative offset to compute contig data in demux buf

b_contig_data() should be called with a head-relative offset (0 for the
beginning of readable data). However, in the FCGI multiplexer, to get
contiguous data available in the demux buffer, it is called with
b_head_ofs(dbuf) which returns an absolute buffer position (b->head). So
b->head is counted twice. Because of this bug, the demux buffer could be
realigned while it should not and conversely.

Instead, the offset 0 must be used. So let's fix it.

This patch must be backported as far as 2.4.

7 days agoBUG/MEDIUM: h1-htx: Sanitize parsing to properly handle upgrade requests
Christopher Faulet [Tue, 26 May 2026 13:35:46 +0000 (15:35 +0200)] 
BUG/MEDIUM: h1-htx: Sanitize parsing to properly handle upgrade requests

Thanks to previous patches, the request messages are now sanitized to
properly handle Upgrade requests. Now, if a 'connection: upgrade' header
value was found while no 'Upgrade' header, the 'upgrade' values is removed
from the 'connection' header. Conversely the opposite is also performed. If
'Upgrade' header was found, but no "conneciotn: upgrade" header value, all
occurrences of 'Upgrade' header are refused.

This patch depends on following ones:
  * MINOR: h1: Add  a H1M flag to specify a non-empty 'Upgrade:' header was parsed
  * MINOR: http: Add function to remove all occurrences of a value in a header

It should fix the issue 3397. But the H2 part should be reviewed too, and
probably the H1 response parsing, to be consistent with this change.

The series should be backported as far as 2.4.

7 days agoMINOR: h1: Add a H1M flag to specify a non-empty 'Upgrade:' header was parsed
Christopher Faulet [Tue, 26 May 2026 13:30:33 +0000 (15:30 +0200)] 
MINOR: h1: Add  a H1M flag to specify a non-empty 'Upgrade:' header was parsed

H1_MF_UPG_HDR flags was introduced to let H1 parser knwon a non-empty 'Upgrade:'
header was parsed.

This patch is mandatory to fix a bug.

7 days agoMINOR: http: Add function to remove all occurrences of a value in a header
Christopher Faulet [Tue, 26 May 2026 13:27:18 +0000 (15:27 +0200)] 
MINOR: http: Add function to remove all occurrences of a value in a header

http_remove_header_value() function was added to parse a header value and
remove all occurrences of a specific value.

This patch is mandatory to fix a bug.

7 days agoBUG/MEDIUM: mux-fcgi: reject stream ID 0 for application records
Christopher Faulet [Tue, 26 May 2026 11:56:12 +0000 (13:56 +0200)] 
BUG/MEDIUM: mux-fcgi: reject stream ID 0 for application records

Records with a stream ID set to 0 are reserved to management records.
However there was no check to trigger an error if an application record is
received with a stream ID to 0. This could lead to crash becausqe management
streams (which are static and immutable) can be modified while processing
application records (STDOUT/STDERR/END_REQUEST).

To fix the issue, An error is returned if the stream ID 0 is set on
GET_VALUES_RESULT or UNKNOWN_TYPE records.

This patch must be backported to all stable versions.

7 days agoBUG/MEDIUM: jwe: substitute random CEK on RSA1_5 decryption failure per RFC 7516...
Remi Tricot-Le Breton [Tue, 26 May 2026 15:26:04 +0000 (17:26 +0200)] 
BUG/MEDIUM: jwe: substitute random CEK on RSA1_5 decryption failure per RFC 7516 #11.5

do_decrypt_cek_rsa() calls EVP_PKEY_decrypt with RSA_PKCS1_PADDING for
RSA1_5 and returns failure (goto end) on decrypt error. This creates a
measurable timing difference between "padding invalid" (fast exit before
content decryption) and "padding valid + AEAD tag fail" (full AES-GCM/CBC
decryption path), exposing the RSA private key to a Bleichenbacher-style
adaptive attack requiring ~10^4-10^6 queries.

Fix: On RSA_PKCS1_PADDING failure, fill decrypted_cek with random bytes
of the buffer size and return success (retval=0). This forces execution
into decrypt_ciphertext() regardless of padding validity, so the attacker
cannot distinguish valid from invalid padding via timing. The AEAD tag
check in decrypt_ciphertext() will still reject the wrong CEK, but the
timing profile is identical for both branches.

RSA-OAEP variants are not affected (mathematically infeasible to craft
valid ciphertext without the private key).

Introduced by RSA1_5 path lacking constant-time fallback.

7 days agoBUG/MINOR: jwe: enforce GCM tag length to 128 bits
Remi Tricot-Le Breton [Tue, 26 May 2026 14:20:52 +0000 (16:20 +0200)] 
BUG/MINOR: jwe: enforce GCM tag length to 128 bits

Two fixes addressing cryptographic and parsing correctness issues:

1. Enforce 16-byte GCM authentication tag in decrypt_ciphertext()

   The base64url-decoded 5th JWE component (authentication tag) was passed
   directly to EVP_CTRL_AEAD_SET_TAG with its attacker-controlled length.
   OpenSSL accepts 1-16 byte GCM tags and only verifies that many bytes, so
   a 1-byte tag reduces forgery work factor to ~256. RFC 7518 mandates 128-bit
   (16 byte) tags for A*GCM. The CBC-HMAC path already enforced correct length,
   confirming this was an oversight.

   Fix: Add (*aead_tag)->data != 16 check before the GCM branch in
   decrypt_ciphertext(), rejecting any non-16-byte tag.

   Introduced by 416b87d5db (JWE A*GCM support).

2. Enforce 16-byte GCMKW tag in parse_jose() decode_jose_field()

   The $.tag field from the attacker-supplied protected header in A*GCMKW
   key-wrap was similarly decoded without length enforcement. Fix: Add a
   size != 16 check for fields named ".tag" in decode_jose_field() when
   called from the GCMKW path.

   Introduced by 026652a7eb (GCMKW tag field parsing).

7 days agoBUG/MINOR: quic: reject packet too short for HP decryption
Amaury Denoyelle [Tue, 26 May 2026 15:21:07 +0000 (17:21 +0200)] 
BUG/MINOR: quic: reject packet too short for HP decryption

Header protection can only be performed on a packet of a minimal size.
There was already a check for this in qc_do_rm_hp() but it did not use
the correct value.

Fix this by using the correct minimal size which is 20 bytes starting
from the packet number offset. This is enough to decrypt 4 bytes (PN max
size) and 16 bytes of IV. If the packet is not big enough, it is
still silently discarded.

This must be backported up to 2.6.

7 days agoBUG/MINOR: qmux: do not crash on frame parsing issue
Amaury Denoyelle [Tue, 26 May 2026 12:25:32 +0000 (14:25 +0200)] 
BUG/MINOR: qmux: do not crash on frame parsing issue

Ensure frame parsing error does not cause a crash by removing the
associated BUG_ON()/ABORT_NOW().

For now, connection is flagged on error, which ensures that any
send/receive future operations are prevented and connection is closed
asap. In the future, a proper CONNECTION_CLOSE will be required as
defined by QMux protocol.

No need to backport.

7 days agoBUG/MINOR: hlua: prevent Lua from passing CR/LF/NUL in HTTP headers
Willy Tarreau [Tue, 26 May 2026 11:49:49 +0000 (13:49 +0200)] 
BUG/MINOR: hlua: prevent Lua from passing CR/LF/NUL in HTTP headers

hlua_http_add_hdr() passes Lua string values directly to htx_add_header()
without validation. This can be an issue for user-controlled data, but as
well when relying on poorly written scripts. This patch makes sure that
neither the name nor the value may contain any of these forbidden chars.

This should be backported to all versions since the issue has been there
since at least 2.4.

7 days agoBUG/MINOR: h3: add missing break on rcv_buf()
Amaury Denoyelle [Tue, 26 May 2026 12:06:23 +0000 (14:06 +0200)] 
BUG/MINOR: h3: add missing break on rcv_buf()

The following patch ensures server MAX_PUSH_ID are rejected as a client.
This has been implemented by extending h3_rcv_buf().

  e4a5a64198bb084eaef2e71bfde65704a5db3931
  BUG/MINOR: h3: reject server MAX_PUSH_ID frame

Case label for MAX_PUSH_ID has been moved in the function, however the
break instruction was removed by error. Fix this by adding the missing
break statement.

This must be backported to every version the above fix is. Currently, it
is scheduled to 3.3.

7 days agoBUG/MEDIUM: auth: fix unconfigured password NULL deref
William Lallemand [Tue, 26 May 2026 12:08:38 +0000 (14:08 +0200)] 
BUG/MEDIUM: auth: fix unconfigured password NULL deref

Fix a case of dereference NULL pointer when trying to use an user from
an userlist which does not have a password configured.

The check_user() function tries to do an strcmp of the password, howver
u->pass is NULL and the strcmp would crash when trying.

Must be backported in every stable branches.

7 days agoBUG/MINOR: h3: reject server MAX_PUSH_ID frame
Amaury Denoyelle [Tue, 26 May 2026 08:36:06 +0000 (10:36 +0200)] 
BUG/MINOR: h3: reject server MAX_PUSH_ID frame

Previously, MAX_PUSH_ID frames were silently ignored both on client and
server sides. However, such frame cannot be emitted by the server.

This patch fixes this by properly issuing connection error
FRAME_UNEXPECTED when receiving a MAX_PUSH_ID frame as a client. This is
implemented by extending h3_check_frame_valid().

This must be backported up to 3.3.

7 days agoBUG/MINOR: h3: adjust error on PUSH_PROMISE frame reception
Amaury Denoyelle [Tue, 26 May 2026 08:53:48 +0000 (10:53 +0200)] 
BUG/MINOR: h3: adjust error on PUSH_PROMISE frame reception

HTTP/3 PUSH_PROMISE frames are systematically rejected with H3 error
FRAME_UNEXPECTED. This is adapted on the server side as a client can
never emit them.

This patch adapts error reporting when haproxy runs as a client. In this
case, server is still forbidden to emit any PUSH_PROMISE as MAX_PUSH_ID
frames are never emitted. In this case, ID_ERROR must be used as an
error code.

This must be backported up to 3.3.

7 days agoBUG/MINOR: h3: reject client CANCEL_PUSH frame
Amaury Denoyelle [Tue, 26 May 2026 09:22:16 +0000 (11:22 +0200)] 
BUG/MINOR: h3: reject client CANCEL_PUSH frame

CANCEL_PUSH frames are silently ignored on both client and server sides.
However, as push support is not implemented by haproxy, clients are thus
forbidden to emit any of those frames.

Fix this by closing the connection with ID_ERROR when receiving a client
CANCEL_PUSH as a server. On client side, the frame is still silently
discarded.

This must be backported up to 2.6.

7 days agoBUG/MINOR: h3: reject server push stream
Amaury Denoyelle [Tue, 26 May 2026 08:41:07 +0000 (10:41 +0200)] 
BUG/MINOR: h3: reject server push stream

Push streams are not supported by haproxy as a client. Thus, it never
emits any MAX_PUSH_ID frame. In this case, the server is not allowed to
initiate any push stream.

This patch ensures that such stream is closed with error H3_ID_ERROR, as
specified by HTTP/3 RFC.

This must be backported up to 3.3.

7 days agoBUG/MEDIUM: h3: reject client push stream
Amaury Denoyelle [Tue, 26 May 2026 08:25:54 +0000 (10:25 +0200)] 
BUG/MEDIUM: h3: reject client push stream

HTTP/3 push streams can only be opened by a server instance. The
specification mandates that the connection must be closed if a server
receives a client-initiated push stream.

This patch should ensure that it is not possible to exploit
unidirectional streams for an unexpected usage.

This must be backported up to 2.6.

7 days agoMEDIUM: tools: switch the main PRNG to a thread-local xoshiro256**
Willy Tarreau [Mon, 25 May 2026 13:38:49 +0000 (15:38 +0200)] 
MEDIUM: tools: switch the main PRNG to a thread-local xoshiro256**

The current PRNG is xoroshiro128**, it was introduced in 2.2 with
commit 52bf83939 ("BUG/MEDIUM: random: implement a thread-safe and
process-safe PRNG").  It features a 2^128 sequence and can perform
2^64 or 2^96 jumps, though only the 2^96 jump is implemented. It
was initially designed to support both processes and threads, and
implements a shared state between threads instead of allocating
distinct sequences based on PID and thread numbers.

Since then, the PRNG's usage grew and processes have disappeared,
but the lock or the DWCAS are still there due to its shared nature,
and it's possible to trigger watchdog warnings by issuing 100 UUIDs
in a single log-format string.

Also, UUID and QUIC retry tokens now consume 128 bits from the PRNG
in two 64-bit calls, and used to weaken the PRNG by rapidly disclosing
its internal state on reasonably idle systems. This indicates that
most of the time we now need 128 bits.

This patch modernizes the internal generator by switching to xoshiro256**,
which has comparable properties (it's even faster), and features even
longer 2^256 periods, still returning 64 bits per call. It can be
initialized with 2^128 and 2^192 jumps. More details here:

   https://prng.di.unimi.it/
   https://prng.di.unimi.it/xoshiro256starstar.c

Here we implement a thread-local state instead of the old shared one,
so there is no more need for synchronization. The state is seeded at
boot, and each thread performs as many 2^192 jumps as their TID is
large. The master process performs a 2^128 jump where it used to
perform a 2^96 jump so that it doesn't overlap with any worker thread.
However a cleaner approach could be to perform a 2^128 jump for each
fork() (here the worker) and 2^192 for each thread. This might be for
a future improvement.

ha_random64_internal() is now the new PRNG, so that everything else
remains totally transparent. _ha_random64_pair_hashed() continues to
hash the first 128 bits of the state.

A simple config generating 100 UUID on 20 threads jumps from 135k to
1.25M req/s, which translates to a bump from 13.5M to 125M UUID/s,
or 9 times faster. And there is no more DWCAS can be seen anymore
in perf top:

Before: 13.5M/s
Overhead  Shared Object            Symbol
  99.04%  haproxy       [.] ha_random64_internal
   0.66%  haproxy       [.] _ha_random64_pair_hashed
   0.03%  libc-2.42.so  [.] __printf_buffer
   0.02%  [kernel]      [k] _raw_spin_lock
   0.01%  libc-2.42.so  [.] __strchrnul_avx2
   0.01%  [kernel]      [k] ktime_get
   0.01%  [kernel]      [k] lapic_next_deadline
   0.01%  haproxy       [.] sample_process
   0.01%  haproxy       [.] chunk_printf
   0.01%  libc-2.42.so  [.] __printf_buffer_write
   0.01%  [kernel]      [k] hrtimer_active
   0.01%  libc-2.42.so  [.] __memmove_avx_unaligned_erms
   0.01%  libc-2.42.so  [.] _itoa_word

After: 125M/s
  18.84%  libc-2.42.so      [.] __printf_buffer
   9.84%  haproxy           [.] sample_process
   8.33%  libc-2.42.so      [.] __strchrnul_avx2
   6.61%  libc-2.42.so      [.] __memmove_avx_unaligned_erms
   6.06%  libc-2.42.so      [.] __printf_buffer_write
   4.43%  haproxy           [.] strlcpy2
   4.09%  libc-2.42.so      [.] _itoa_word
   2.62%  haproxy           [.] sess_build_logline_orig
   2.12%  haproxy           [.] _ha_random64_pair_hashed
   1.28%  haproxy           [.] pool_put_to_cache
   1.06%  haproxy           [.] __pool_alloc
   1.00%  haproxy           [.] smp_fetch_uuid
   0.93%  haproxy           [.] lf_text_len
   0.82%  haproxy           [.] ha_generate_uuid_v4

7 days agoMEDIUM: quic: use ha_random64_pair_hashed() to generate the QUIC retry tokens
Willy Tarreau [Mon, 25 May 2026 16:31:02 +0000 (18:31 +0200)] 
MEDIUM: quic: use ha_random64_pair_hashed() to generate the QUIC retry tokens

The QUIC retry tokens used to directly return ha_random64(), making the
next tokens easily predictable on low-load systems before the XXH64 call.
Let's now switch to the faster and safer ha_random64_pair_hashed() instead.

7 days agoMEDIUM: h1: use ha_random64_pair_hashed() for the WebSocket key
Willy Tarreau [Mon, 25 May 2026 16:23:30 +0000 (18:23 +0200)] 
MEDIUM: h1: use ha_random64_pair_hashed() for the WebSocket key

Instead of using two consecutive calls to ha_random64(), let's use the
cleaner and safer ha_random64_pair_hashed(). This way the internal
PRNG state will not leak into the emitted headers.

7 days agoMEDIUM: tools: use the hashed random pair for UUID generation
Willy Tarreau [Mon, 25 May 2026 15:56:01 +0000 (17:56 +0200)] 
MEDIUM: tools: use the hashed random pair for UUID generation

The UUID generation used to emit the internal PRNG state, which allows
to predict previous and next ones, or disclose the internal PRNG state.
While not critical, it may eventually become an issue.

This patch uses the new ha_random64_pair_hashed() function that returns
a pair of u64 that are hashed from the internal PRNG state. It's almost
twice as fast on 20 threads (14.1M UUID/s vs 7.8M/s).

7 days agoMEDIUM: init: fall back to ha_random64_pair_hashed() for the cluster secret
Willy Tarreau [Mon, 25 May 2026 16:20:29 +0000 (18:20 +0200)] 
MEDIUM: init: fall back to ha_random64_pair_hashed() for the cluster secret

The cluster secret, when SSL is not working, used to involve a mix of
calls to ha_random64() and random() to mask the bits that we didn't want
to see leaked. Let's now simply fall back to ha_random64_pair_hashed()
that does a much better job.

7 days agoMINOR: tools: provide a function to generate a hashed random pair
Willy Tarreau [Mon, 25 May 2026 15:30:58 +0000 (17:30 +0200)] 
MINOR: tools: provide a function to generate a hashed random pair

A lot of places call two ha_random64() in a row to generate a 128-bit
random. While it's now safe against linear analysis thanks to the XXH64
call, it's still particularly expensive due to the lock.

Here we introduce a new function ha_random64_pair_hashed(), that feeds
two uint64_t with a hash of the PRNG's internal state, and make it
advance. This will cut in half the number of calls to ha_random64()
and should recover a part of the performance lost in the lock. For
now it's not used.

7 days agoBUG/MEDIUM: tools: insert an XXH64 layer on the PRNG output
Willy Tarreau [Mon, 25 May 2026 15:30:58 +0000 (17:30 +0200)] 
BUG/MEDIUM: tools: insert an XXH64 layer on the PRNG output

Consuming randoms in pairs directly exposes the internal PRNG's state
on moderately idle system. It can allow to predict next (or previous)
UUIDs, QUIC retry tokens, and WS keys for example. Let's insert an XXH64
call on the ha_random64() output to avoid this. We expand the boot seed
as the secret at boot, and use now_ns as the seed for each call. The
original ha_random64() function was renamed to ha_random64_internal()
for use cases where it's not a problem to directly use the internal
state.

The performance loss is only measurable when single-threaded. It drops
from 7.32M UUID per second to 7.16M. Above that there is no longer any
difference due to the DWCAS loop which reaches up to 98.5% CPU at 20
threads.

This will need to be backported to stable releases after a period of
observation.

7 days agoBUG/MINOR: addons/51d: NUL-terminate headers before passing them to Trie API
Willy Tarreau [Tue, 26 May 2026 07:36:12 +0000 (09:36 +0200)] 
BUG/MINOR: addons/51d: NUL-terminate headers before passing them to Trie API

_51d_set_device_offsets() passes ctx.value.ptr directly to
fiftyoneDegreesGetDeviceOffset() which expects a null-terminated string.
Let's copy it through the trash first, to avoid possibly surronding
garbage.

This can be backported to all versions.

7 days agoBUG/MINOR: resolvers: switch to a better PRNG for query IDs
Willy Tarreau [Tue, 26 May 2026 07:30:12 +0000 (09:30 +0200)] 
BUG/MINOR: resolvers: switch to a better PRNG for query IDs

The PRNG used by the DNS currently is easily predictable once an
observer can collect a few consecutive IDs from the same thread, since
it's a 32-bit xorshift reduced to 16 bits output. Let's switch it to
ha_random32() instead.

This should be backported, however on older releases the ha_random32()
cost is higher due to the lock involved.

7 days agoBUG/MINOR: ssl-hello: make use of the null-terminated servername
Willy Tarreau [Tue, 26 May 2026 07:23:48 +0000 (09:23 +0200)] 
BUG/MINOR: ssl-hello: make use of the null-terminated servername

In ssl_sock_switchctx_cbk(), the servername is copied into the trash
and null-terminated, but later in the call to strncpy() it's still used
as-is, so anything that follows it will be copied as well, which is not
really expected. Let's make the servername point to the trash after
sanitizing it, like ssl_sock_switchcbk_wolfSSL_cbk() does.

This can be backported to 2.6 since it was introduced with commit
a996763619 ("BUG/MINOR: ssl: Store client SNI in SSL context in case
of ClientHello error").

7 days agoBUG/MINOR: payload: fix the handshake length bounds check smp_client_hello_parse()
Willy Tarreau [Tue, 26 May 2026 07:06:03 +0000 (09:06 +0200)] 
BUG/MINOR: payload: fix the handshake length bounds check smp_client_hello_parse()

After reading the handshake length, which is covered by the previous
4 bytes check, the size was not subtracted before being compared to the
retrieved handshake length, making it possible to accept a handshake
that claims to be 4 bytes larger than it really is. Similarly, a few
lines later, data[34] is accessed without checking that it is present,
because the test is made on the second hs_len, which doesn't guarantee
that the data are there.

This fix adds both tests. It can be backported to all stable versions
as it was introduced in 1.6 with commit bb2acf589f ("MINOR: payload:
add support for tls session ticket ext").

7 days agoBUG/MINOR: base64: return empty string for empty input in base64dec()
Willy Tarreau [Tue, 26 May 2026 06:54:15 +0000 (08:54 +0200)] 
BUG/MINOR: base64: return empty string for empty input in base64dec()

Right now no special case is made of size zero and the parser assumes
that it can read the last two chars, which do not exist in this case.
Let's check for this empty string situation and return zero (empty) as
well.

This should be backported to all versions.