Commit graph

8848 commits

Author SHA1 Message Date
Frederic Lecaille
89c75b0777 BUG/MINOR: quic: missing app ops init during backend 0-RTT sessions
The QUIC mux requires "application operations" (app ops), which are a list
of callbacks associated with the application level (i.e., h3, h0.9) and
derived from the ALPN. For 0-RTT, when the session cache cannot be reused
before activation, the current code fails to reach the initialization of
these app ops, causing the mux to crash during its initialization.

To fix this, this patch restores the behavior of
ssl_sock_srv_try_reuse_sess(), whose purpose was to reuse sessions stored
in the session cache regardless of whether 0-RTT was enabled, prior to
this commit:

  MEDIUM: quic-be: modify ssl_sock_srv_try_reuse_sess() to reuse backend
  sessions (0-RTT)

With this patch, this function now does only one thing: attempt to reuse a
session, and that's it!

This patch allows ignoring whether a session was successfully reused from
the cache or not. This directly fixes the issue where app ops
initialization was skipped upon a session cache reuse failure. From a
functional standpoint, starting a mux without reusing the session cache
has no negative impact; the mux will start, but with no early data to
send.

Finally, there is the case where the ALPN is reset when the backend is
stopped. It is critical to continue locking read access to the ALPN to
secure shared access, which this patch does. It is indeed possible for the
server to be stopped between the call to connect_server() and
quic_reuse_srv_params(). But this cannot prevent the mux to start
without app ops. This is why a 'TODO' section was added, as a reminder that a
race condition regarding the ALPN reset still needs to be fixed.

Must be backported to 3.3
2026-02-25 11:13:52 +01:00
Maxime Henrion
a9dc8e2587 MINOR: quic: add a new metric for ncbuf failures
Some checks failed
Contrib / build (push) Has been cancelled
alpine/musl / gcc (push) Has been cancelled
VTest / Generate Build Matrix (push) Has been cancelled
Windows / Windows, gcc, all features (push) Has been cancelled
VTest / (push) Has been cancelled
This counts the number of times we failed to add data to the ncbuf
buffer because of the gap size limit.
2026-02-23 17:47:45 +01:00
Willy Tarreau
868dd3e88b MINOR: traces: always mark trace_source as thread-aligned
Some perf profiles occasionally show that reading the trace source's
state can take some time, which is not expected at all. It just happens
that the trace_source is not cache-aligned so depending on linkage, it
may share a cache line with a more active variable, thereby inducing a
slow down to all threads trying to read the variable.

Let's always mark it aligned to avoid this. For now the problem was not
observed again.
2026-02-23 16:22:59 +01:00
Amaury Denoyelle
b8cb8e1a65 BUG/MINOR: quic: fix counters used on BE side
quic_conn is initialized with a pointer to its proxy counters. These
counters are then updated during the connection lifetime.

Counters pointer was incorrect for backend quic_conn, as it always
referenced frontend counters. For pure backend, no stats would be
updated. For listen instances, this resulted in incorrect stats
reporting.

Fix this by correctly set proxy counters based on the connection side.

This must be backported up to 3.3.
2026-02-20 14:08:27 +01:00
Amaury Denoyelle
de5fc2f515 BUG/MINOR: server: set auto SNI for dynamic servers
Auto SNI configuration is configured during check config validity.
However, nothing was implemented for dynamic servers.

Fix this by implementing auto SNI configuration during "add server" CLI
handler. Auto SNI configuration code is moved in a dedicated function
srv_configure_auto_sni() called both for static and dynamic servers.

Along with this, allows the keyword "no-sni-auto" on dynamic servers, so
that this process can be deactivated if wanted. Note that "sni-auto"
remains unavailable as it only makes sense with default-servers which
are never used for dynamic server creation.

This must be backported up to 3.3.
2026-02-20 09:02:47 +01:00
Aurelien DARRAGON
2b7849fd02 BUG/MINOR: stats-file: manipulate shm-stats-file heartbeat using unsigned int
shm-stats-file heartbeat is derived from now_ms with an extra time added
to it, thus it should be handled using the same time as now_ms is.

Until now, we used to handle heartbeat using signed integer. This was not
found to cause severe harm but it could result in improper handling due
to early wrapping because of signedness for instance, so let's better fix
that before it becomes a real issue.

It should be backported in 3.3
2026-02-19 16:13:55 +01:00
Frederic Lecaille
c9d47804d1 MINOR: haterm: add haterm HTTP server
Contrary to haproxy, httpterm does not support all the HTTP protocols.
Furthermore, it has become easier to handle inbound/outbound
connections / streams since the rework done at conn_stream level.

This patch implements httpterm HTTP server services into haproxy. To do
so, it proceeds the same way as for the TCP checks which use only one
stream connector, but on frontend side.

The makefile is modified to handle haterm.c in additions to all the C
files for haproxy to build new haterm program into haproxy, the haterm
server also instantiates a haterm stream (hstream struct) attached to a
stream connector for each incoming connection without backend stream
connector. This is the role of sc_new_from_endp() called by the muxes to
instantiate streams/hstreams.

As for stream_new(), hstream_new() instantiates a task named
process_hstream() (see haterm.c) which has the same role as
process_stream() but for haterm streams.

haterm into haproxy takes advantage of the HTTP muxes and HTX API to
support all the HTTP protocols supported by haproxy.
2026-02-19 15:10:37 +01:00
Frederic Lecaille
2bf091e9da MINOR: stconn: stream instantiation from proxy callback
Add a pointer to function to proxies as ->stream_new_from_sc proxy
struct member to instantiate stream from connection as this is done by
all the muxes when they call sc_new_from_endp(). The default value for
this pointer is obviously stream_new() which is exported by this patch.
2026-02-19 14:46:49 +01:00
Frederic Lecaille
6013f4baeb MINOR: init: allow a fileless init mode
This patch provides the possibility to initialize haproxy without
configuration file. This may be identified by the new global and exported
<fileless_mode> and <fileless_cfg> variables which may be used to
provide a struct cfgfile to haproxy by others means than a physical
file (built in memory).
When enabled, this fileless mode skips all the configuration files
parsing.
2026-02-19 14:46:49 +01:00
Frederic Lecaille
234ce775c3 MINOR: trace: add definitions for haterm streams
Add definitions for haterm stream as arguments to be used by the TRACE API.
This will be used by the haterm module to come which will have to handle
hstream struct objects (in place of stream struct objects).
2026-02-19 14:46:49 +01:00
Frederic Lecaille
5d3bca4b17 MINOR: ssl/ckch: certificates generation from "load" "crt-store" directive
Add "generate-dummy" on/off type keyword to "load" directive to
automatically generate dummy certificates as this is done for ACME from
ckch_conf_load_pem_or_generate() function which is called if a "crt"
keyword is also provide for this directive.

Also implement "keytype" to specify the key type used for these
certificates.  Only "RSA" or "ECDSA" is accepted. This patch also
implements "bits" keyword for the "load" directive to specify the
private key size used for RSA. For ECDSA, a new "curves" keyword is also
provided by this patch to specify the curves to be used for the EDCSA
private keys generation.

ckch_conf_load_pem_or_generate() is modified to use these parameters
provided by "keytype", "bits" and "curves" to generate the private key
with ssl_gen_EVP_PKEY() before generating the X509 certificate calling
ssl_gen_x509().
2026-02-19 14:46:49 +01:00
Frederic Lecaille
36b1fba871 MINOR: ssl/ckch: Move EVP_PKEY and cert code generation from acme
Move acme_EVP_PKEY_gen() implementation to ssl_gencrt.c and rename it to
ssl_EVP_PKEY_gen().  Also extract from acme_gen_tmp_x509() the generic
part to implement ssl_gen_x509() into ssl_gencrt.c.

To generate a self-signed expired certificate ssl_EVP_PKEY_gen() must be
used to generate the private key. Then, ssl_gen_x509() must be called
with the private key as argument.  acme_gen_tmp_x509() is also modified
to called these two functions to generate a temporary certificate has
done before modifying this part.

Such an expired self-signed certificate should not be use on the field
but only during testing and development steps.
2026-02-19 14:46:47 +01:00
Nenad Merdanovic
5a079d1811 MEDIUM: Add connect/queue/tarpit timeouts to set-timeout
Add the ability to set connect, queue and tarpit timeouts from the
set-timeout action. This is especially useful when using set-dst to
dynamically connect to servers.

This patch also adds the relevant fe_/be_/cur_ sample fetches for these
timeouts.
2026-02-19 08:20:37 +01:00
Christopher Faulet
a324616cdb MINOR: dynbuf: Add helpers to know if a buffer is a default or a large buffer
b_is_default() and b_is_large() can now be used to know if a buffer is a
default buffer or a large one. _b_free() now relies on it.

These functions are also used when possible (stream_free(),
stream_release_buffers() and http_wait_for_msg_body()).
2026-02-18 13:26:21 +01:00
Christopher Faulet
5737fc9518 MEDIUM: http-ana: Use a large buffer if necessary when waiting for body
Thanks to previous patches, it is now possible to allocate a large buffer to
store the message payload in the context of the "wait-for-body" action. To
do so, "use-large-buffer" option must be set.

It means now it is no longer necessary to increase the regular buffer size
to be able to get message payloads of some requests or responses.
2026-02-18 13:26:21 +01:00
Christopher Faulet
ce912271db MEDIUM: chunk: Add support for large chunks
Because there is now a memory pool for large buffers, we must also add the
support for large chunks. So, if large buffers are configured, a dedicated
memory pool is created to allocate large chunks. alloc_large_trash_chunk()
must be used to allocate a large chunk. alloc_trash_chunk_sz() can be used to
allocate a chunk with the best size. However free_trash_chunk() remains the
only way to release a chunk, regular or large.

In addition, large trash buffers are also created, using the same mechanism
than for regular trash buffers. So three thread-local trash buffers are
created. get_large_trash_chunk() must be used to get a large trash buffer.
And get_trash_chunk_sz() may be used to get a trash buffer with the best
size.
2026-02-18 13:26:21 +01:00
Christopher Faulet
d89ec33a34 MEDIUM: dynbuf: Add a pool for large buffers with a configurable size
Add the support for large bufers. A dedicated memory pool is added. The size
of these buffers must be explicitly configured by setting
"tune.bufsize.large" directive. If it is not set, the pool is not
created. In addition, if the size for large buffers is the same than for
regular buffer, the feature is automatically disable.

For now, large buffers remain unused.
2026-02-18 13:26:21 +01:00
Christopher Faulet
4f27a72d19 MEDIUM: htx: Improve detection of fragmented/unordered HTX messages
First, an HTX flags was added to know when blocks are unordered. It may
happen when a header is added while part of the payload was already received
or when the start-line is replaced by an new one. In these cases, the blocks
indexes are in the right order but not the blocks payload. Knowing a message
is unordered can be useful to trigger a defragmentation, mainly to be able
to append data properly for instance.

Then, detection of fragmented messages was improved, especially when a
header or a start-line is replaced by a new one.

Finally, when data are added in a message and cannot be appended into the
previous DATA block because the message is not aligned, a defragmentation is
performed to realign the message and append data.
2026-02-18 13:26:21 +01:00
Christopher Faulet
53b7150357 MEDIUM: stream: Limit number of synchronous send per stream wakeup
It is not a bug fix, because there is no way to hit the issue for now. But
there is nothing preventing a loop of synchronous sends in process_stream().
Indead, when a synchronous send is successfully performed, we restart the
SCs evaluation and at the end another synchronous send is attempted. So with
an endpoint consuming data bit by bit or with a filter fowarding few bytes
at each call, it is possible to loop for a while in process_stream().

Because it is not expected, we now limit the number of synchronous send per
wakeup to two calls. In a nominal case, it should never be more. This commit
is mandatory to be able to handle large buffers on channels

There is no reason to backport this commit except if the large buffers
support on channels are backported.
2026-02-18 13:26:21 +01:00
Christopher Faulet
6bf450b7fe MINOR: tree-wide: Use the buffer size instead of global setting when possible
At many places, we rely on global.tune.bufsize value instead of using the buffer
size. For now, it is not a problem. But if we want to be able to deal with
buffers of different sizes, it is good to reduce as far as possible dependencies
on the global value. most of time, we can use b_size() or c_size()
functions. The main change is performed on the error snapshot where the buffer
size was added into the error_snapshot structure.
2026-02-18 13:26:20 +01:00
Christopher Faulet
806c8c830d REORG: stconn: Move functions related to channel buffers to sc_strm.h
sc_have_buff(), sc_need_buff(), sc_have_room() and sc_need_room() are
related to the buffer's channel. So we can move them in sc_strm.h header
file. In addition, this will be mandatory for the next commit.
2026-02-18 09:44:16 +01:00
Christopher Faulet
1b1a0b3bae MINOR: stconn: Add missing SC_FL_NO_FASTFWD flag in sc_show_flags
SC_FL_NO_FASTFWD flag was not listed in sc_show_flags() function. Let's do
so.

This patch could be backported as far as 3.0.
2026-02-18 09:44:16 +01:00
Amaury Denoyelle
f3003d1508 BUG/MAJOR: Revert "MEDIUM: mux-quic: add BUG_ON if sending on locally closed QCS"
This reverts commit 235e8f1afd.

Prior to the above commit, snd_buf callback for QUIC MUX was able to
deal with data even after stream closure. The excess was simply
discarded, as no STREAM frame can be emitted after FIN/RESET_STREAM.
This code was later removed and replaced by a BUG_ON() to ensure snd_buf
is never called after stream closure.

However, this approach is too strict. Indeed, there is nothing in the
haproxy stream architecture which forbids this scheduling, in part
because QUIC MUX is the sole responsible of the stream closure. As such,
it is preferable to revert to the old code to prevent any triggering of
a BUG_ON() failure.

Note that nego_ff does not implement data draining if called after
stream closure. This will be done in a future patch.

Thanks to Mike Walker for his investigation on the subject.

This must be backported up to 2.8.
2026-02-17 18:18:44 +01:00
William Lallemand
df8e05815c BUG/MINOR: ssl: lack crtlist_dup_ssl_conf() declaration
Add lacking crtlist_dup_ssl_conf() declaration in ssl_crt-list.h.

Could be backported if needed.
2026-02-16 16:04:35 +01:00
Aurelien DARRAGON
d71e2e73ea MEDIUM: filters: use per-channel filter list when relevant
Some checks failed
Contrib / build (push) Has been cancelled
alpine/musl / gcc (push) Has been cancelled
VTest / Generate Build Matrix (push) Has been cancelled
Windows / Windows, gcc, all features (push) Has been cancelled
VTest / (push) Has been cancelled
In the historical implementation, all filter related information where
stored at the stream level (using struct strm_flt * context), and filters
iteration was performed at the stream level also.

We identified that this was not ideal and would make the implementation of
future filters more complex since filters ordering should be handled in
a different order during request and response handling for decompression
for instance.

To make such thing possible, in this commit we migrate some channel
specific filter contexts in the channel directly (request or response),
and we implement 2 additional filter lists, one on the request channel
and another on the response channel. The historical stream filter list
is kept as-is because in some contexts only the stream is available and
we have to iterate on all filters. But for functions where we only are
interested in request side or response side filters, we now use dedicated
channel filters list instead.

The only overhead is that the "struct filter" was expanded by two "struct
list".

For now, no change of behavior is expected.
2026-02-13 12:24:13 +01:00
Aurelien DARRAGON
bb6cfbe754 MINOR: filters: rework filter iteration for channel related callback functions
Multiple channel related functions have the same construction: they use
list_for_each_entry() to work on a given filter from the stream+channel
combination. In future commits we will try to use filter list from
dedicated channel list instead of the stream one, thus in this patch we
need as a prerequisite to implement and use the flt_list_{start,next} API
to iterate over filter list, giving the API the responsibility to iterate
over the correct list depending on the context, while the calling function
remains free to use the iteration construction it needs. This way we will
be able to easily change the way we iterate over filter list without
duplicating the code for requests and responses.
2026-02-13 12:24:07 +01:00
Egor Shestakov
f5f9c008b1 CLEANUP: initcall: adjust comments to INITCALL{0,1} macros
Some checks are pending
Contrib / build (push) Waiting to run
alpine/musl / gcc (push) Waiting to run
VTest / Generate Build Matrix (push) Waiting to run
VTest / (push) Blocked by required conditions
Windows / Windows, gcc, all features (push) Waiting to run
It is likely that copy-pasted text was not initially adjusted in the
comments, that is misleading regarding number of arguments.

No backport needed.
2026-02-11 09:10:56 +01:00
William Lallemand
ea92b0ef01 BUG/MINOR: ssl: SSL_CERT_DIR environment variable doesn't affect haproxy
Some checks are pending
Contrib / build (push) Waiting to run
alpine/musl / gcc (push) Waiting to run
VTest / Generate Build Matrix (push) Waiting to run
VTest / (push) Blocked by required conditions
Windows / Windows, gcc, all features (push) Waiting to run
The documentation of @system-ca specifies that one can overwrite the
value provided by the SSL Library using SSL_CERT_DIR.

However it seems like X509_get_default_cert_dir() is not affected by
this environment variable, and X509_get_default_cert_dir_env() need to
be used in order to get the variable name, and get the value manually.

This could be backported in every stable branches. Note that older
branches don't have the memprintf in ssl_sock.c.
2026-02-10 21:34:45 +01:00
Willy Tarreau
e2631ee5f7 MEDIUM: activity: apply and use new finegrained task profiling settings
In continuity of previous patch, this one makes use of the new profiling
flags. For this, based on the global "profiling" setting, when switching
profiling on, we set or clear two flags on the thread context,
TH_FL_TASK_PROFILING_L and TH_FL_TASK_PROFILING_M to indicate whether
lock profiling and/or malloc profiling are desired when profiling is
enabled. These flags are checked along with TH_FL_TASK_PROFILING to
decide when to collect time around a lock or a malloc. And by default
we're back to the behavior of 3.2 in that neither lock nor malloc times
are collected anymore.

This is sufficient to see the CPU usage spent in the VDSO to significantly
drop from 22% to 2.2% on a highly loaded system.

This should be backported to 3.3 along with the previous patch.
2026-02-10 17:52:59 +01:00
Willy Tarreau
a7b2353cb3 MINOR: activity: support setting/clearing lock/memory watching for task profiling
Damien Claisse reported in issue #3257 a performance regression between
3.2 and 3.3 when task profiling is enabled, more precisely in relation
with the following patches were merged:

  98cc815e3e ("MINOR: activity: collect time spent with a lock held for each task")
  503084643f ("MINOR: activity: collect time spent waiting on a lock for each task")
  9d8c2a888b ("MINOR: activity: collect CPU time spent on memory allocations for each task")

The issue mostly comes from the first patches. What happens is that the
local time is taken when entering and leaving each lock, which costs a
lot on a contended system. The problem here is the lack of finegrained
settings for lock and malloc profiling.

This patch introduces a better approach. The task profiler goes back to
its default behavior in on/auto modes, but the configuration now accepts
new extra options "lock", "no-lock", "memory", "no-memory" to precisely
indicate other timers to watch for each task when profiling turns on.

This is achieved by setting two new flags HA_PROF_TASKS_LOCK and
HA_PROF_TASKS_MEM in the global "profiling" variable.

This patch only parses the new values and assigns them to the global
variable from the config file for now. The doc was updated.
2026-02-10 17:47:02 +01:00
Willy Tarreau
64c5d45a26 BUG/MEDIUM: lb-chash: always properly initialize lb_nodes with dynamic servers
Some checks are pending
Contrib / build (push) Waiting to run
alpine/musl / gcc (push) Waiting to run
VTest / Generate Build Matrix (push) Waiting to run
VTest / (push) Blocked by required conditions
Windows / Windows, gcc, all features (push) Waiting to run
An issue was introduced in 3.0 with commit faa8c3e024 ("MEDIUM: lb-chash:
Deterministic node hashes based on server address"): the new server_key
field and lb_nodes entries initialization were not updated for servers
added at run time with "add server": server_key remains zero and the key
used in lb_node remains the one depending only on the server's ID.

This will cause trouble when adding new servers with consistent hashing,
because the hash-key will be ignored until the server's weight changes
and the key difference is detected, leading to its recalculation.

This is essentially caused by the poorly placed lb_nodes initialization
that is specific to lb-chash and had to be replicated in the code dealing
with server addition.

This commit solves the problem by adding a new ->server_init() function
in the lbprm proxy struct, that is called by the server addition code.
This also allows to abandon the complex check for LB algos that was
placed there for that purpose. For now only lb-chash provides such a
function, and calls it as well during initial setup. This way newly
added servers always use the correct key now.

While it should also theoretically have had an impact on servers added
with the "random" algorithm, it's unlikely that the difference between
proper server keys and those based on their ID could have had any visible
effect.

This patch should be backported as far as 3.0. The backport may be eased
by a preliminary backport of previous commit "CLEANUP: lb-chash: free
lb_nodes from chash's deinit(), not global", though this is not strictly
necessary if context is manually adjusted.
2026-02-10 07:22:54 +01:00
Amaury Denoyelle
5753c14e84 MINOR: proxy: assign dynamic proxy ID
Implement proxy ID generation for dynamic backends. This is performed
through the already function existing proxy_get_next_id().

As an optimization, lookup will performed starting from a global
variable <dynpx_next_id>. It is initialized to the greatest ID assigned
after parsing, and updated each time a backend instance is created. When
backend deletion will be implemented, it could be lowered to the newly
available slot.
2026-02-06 17:28:27 +01:00
Amaury Denoyelle
e152913327 MINOR: proxy: parse mode on dynamic backend creation
Add an optional "mode" argument to "add backend" CLI command. This
argument allows to specify if the backend is in TCP or HTTP mode.

By default, it is mandatory, unless the inherited default proxy already
explicitely specifies the mode. To differentiate if TCP mode is implicit
or explicit, a new proxy flag PR_FL_DEF_EXPLICIT_MODE is defined. It is
set for every defaults instances which explicitely defined their mode.
2026-02-06 17:27:50 +01:00
Amaury Denoyelle
817003aa31 MINOR: backend: add function to check support for dynamic servers
Move backend compatibility checks performed during 'add server' in a
dedicated function be_supports_dynamic_srv(). This should simplify
addition of future restriction.

This function will be reused when implementing backend creation at
runtime.
2026-02-06 14:35:19 +01:00
Amaury Denoyelle
dc6cf224dd MINOR: proxy: refactor mode parsing
Define a new utility function str_to_proxy_mode() which is able to
convert a string into the corresponding proxy mode if possible. This new
function is used for the parsing of "mode" configuration proxy keyword.

This patch will be reused for dynamic backend implementation, in order
to parse a similar "mode" argument via a CLI handler.
2026-02-06 14:35:18 +01:00
Amaury Denoyelle
87ea407cce MINOR: proxy: refactor proxy inheritance of a defaults section
If a proxy is referencing a defaults instance, some checks must be
performed to ensure that inheritance will be compatible. Refcount of the
defaults instance may also be incremented if some settings cannot be
copied. This operation is performed when parsing a new proxy of defaults
section which references a defaults, either implicitely or explicitely.

This patch extracts this code into a dedicated function named
proxy_ref_defaults(). This in turn may call defaults_px_ref()
(previously called proxy_ref_defaults()) to increment its refcount.

The objective of this patch is to be able to reuse defaults inheritance
validation for dynamic backends created at runtime, outside of the
parsing code.
2026-02-06 14:35:18 +01:00
Amaury Denoyelle
a8bc83bea5 MINOR: cfgparse: move proxy post-init in a dedicated function
A lot of proxies initialization code is delayed on post-parsing stage,
as it depends on the configuration fully parsed. This is performed via a
loop on proxies_list.

Extract this code in a dedicated function proxy_finalize(). This patch
will be useful for dynamic backends creation.

Note that for the moment the code has been extracted as-is. With each
new features, some init code was added there. This has become a giant
loop with no real ordering. A future patch may provide some cleanup in
order to reorganize this.
2026-02-06 14:35:18 +01:00
Amaury Denoyelle
2c8ad11b73 MINOR: cfgparse: validate defaults proxies separately
Default proxies validation occurs during post-parsing. The objective is
to report any tcp/http-rules which could not behave as expected.

Previously, this was performed while looping over standard proxies list,
when such proxy is referencing a default instance. This was enough as
only named referenced proxies were kept after parsing. However, this is
not the case anymore in the context of dynamic backends creation at
runtime.

As such, this patch now performs validation on every named defaults
outside of the standard proxies list loop. This should not cause any
behavior difference, as defaults are validated without using the proxy
which relies on it.

Along with this change, PR_FL_READY proxy flag is now removed. Its usage
was only really needed for defaults, to avoid validating a same instance
multiple times. With the validation of defaults in their own loop, it is
now redundant.
2026-02-06 14:35:18 +01:00
Olivier Houchard
f26562bcb7 MINOR: quic: Fix build with USE_QUIC_OPENSSL_COMPAT
Commit fa094d0b61 changed the msg callback
args, but forgot to fix quic_tls_msg_callback() accordingly, so do that,
and remove the unused struct connection paramter.
2026-02-03 04:05:34 +01:00
William Lallemand
23e8ed6ea6 MEDIUM: ssl: porting to X509_STORE_get1_objects() for OpenSSL 4.0
OpenSSL 4.0 is deprecating X509_STORE_get0_objects().

Every occurence of X509_STORE_get0_objects() was first replaced by
X509_STORE_get1_objects().
This changes the ref count of the STACK_OF(X509_OBJECT) everywhere, and
need it to be sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free) each time.

X509_STORE_get1_objects() is not available in AWS-LC, OpenSSL < 3.2,
LibreSSL and WolfSSL, so we need to still be compatible with get0.
To achieve this, 2 macros were added X509_STORE_getX_objects() and
sk_X509_OBJECT_popX_free(), these macros will use either the get0 or the
get1 macro depending on their availability. In the case of get0,
sk_X509_OBJECT_popX_free() will just do nothing instead of trying to
free.

Don't backport that unless really needed if we want to be compatible
with OpenSSL 4.0. It changes all the refcounts.
2026-01-29 17:08:41 +01:00
Amaury Denoyelle
fa094d0b61 MEDIUM: ssl: remove connection from msg callback args
Some checks failed
Contrib / build (push) Has been cancelled
alpine/musl / gcc (push) Has been cancelled
VTest / Generate Build Matrix (push) Has been cancelled
Windows / Windows, gcc, all features (push) Has been cancelled
VTest / (push) Has been cancelled
SSL msg callbacks are used for notification about sent/received SSL
messages. Such callbacks are registered via
ssl_sock_register_msg_callback().

Prior to this patch, connection was passed as first argument of these
callbacks. However, most of them do not use it. Worst, this may lead to
confusion as connection can be NULL in QUIC context.

This patch cleans this by removing connection argument. As an
alternative, connection can be retrieved in callbacks if needed using
ssl_sock_get_conn() but the code must be ready to deal with potential
NULL instances. As an example, heartbeat parsing callback has been
adjusted in this manner.
2026-01-29 11:14:09 +01:00
Willy Tarreau
35d63cc3c7 MEDIUM: h1: strictly verify quoting in chunk extensions
Some checks are pending
Contrib / build (push) Waiting to run
alpine/musl / gcc (push) Waiting to run
VTest / Generate Build Matrix (push) Waiting to run
VTest / (push) Blocked by required conditions
Windows / Windows, gcc, all features (push) Waiting to run
As reported by Ben Kallus in the following thread:

   https://www.mail-archive.com/haproxy@formilux.org/msg46471.html

there exist some agents which mistakenly accept CRLF inside quoted
chunk extensions, making it possible to fool them by injecting one
extra chunk they won't see for example, or making them miss the end
of the body depending on how it's done. Haproxy, like most other
agents nowadays, doesn't care at all about chunk extensions and just
drops them, in agreement with the spec.

However, as discussed, since chunk extensions are basically never used
except for attacks, and that the cost of just matching quote pairs and
checking backslashed quotes is escape consistency remains relatively
low, it can make sense to add such a check to abort the message parsing
when this situation is encountered. Note that it has to be done at two
places, because there is a fast path and a slow path for chunk parsing.

Also note that it *will* cause transfers using improperly formatted chunk
extensions to fail, but since these are really not used, and that the
likelihood of them being used but improperly quoted certainly is much
lower than the risk of crossing a broken parser on the client's request
path or on the server's response path, we consider the risk as
acceptable. The test is not subject to the configurable parser exceptions
and it's very unlikely that it will ever be needed.

Since this is done in 3.4 which will be LTS, this patch will have to be
backported to 3.3 so that any unlikely trouble gets a chance to be
detected before users upgrade to 3.4.

Thanks to Ben for the discussion, and to Rajat Raghav for sparking it
in the first place even though the original report was mistaken.

Cc: Ben Kallus <benjamin.p.kallus.gr@dartmouth.edu>
Cc: Rajat Raghav <xclow3n@gmail.com>
Cc: Christopher Faulet <cfaulet@haproxy.com>
2026-01-28 18:54:23 +01:00
Willy Tarreau
a9df6947b4 OPTIM: proxy: separate queues fields from served
There's still a lot of contention when accessing the backend's
totpend and queueslength for every request in may_dequeue_tasks(),
even when queues are not used. This only happens because it's stored
in the same cache line as >beconn which is being written by other
threads:

  0.01 |        call       sess_change_server
  0.02 |        mov        0x188(%r15),%esi     ## s->queueslength
       |      if (may_dequeue_tasks(srv, s->be))
  0.00 |        mov        0xa8(%r12),%rax
  0.00 |        mov        -0x50(%rbp),%r11d
  0.00 |        mov        -0x60(%rbp),%r10
  0.00 |        test       %esi,%esi
       |        jne        3349
  0.01 |        mov        0xa00(%rax),%ecx     ## p->queueslength
  8.26 |        test       %ecx,%ecx
  4.08 |        je         288d

This patch moves queueslength and totpend to their own cache line,
thus adding 64 bytes to the struct proxy, but gaining 3.6% of RPS
on a 64-core EPYC thanks to the elimination of this false sharing.

process_stream() goes down from 3.88% to 3.26% in perf top, with
the next top users being inc/dec (s->served) and be->beconn.
2026-01-28 16:07:27 +00:00
Willy Tarreau
3ca2a83fc0 OPTIM: server: move queueslength in server struct
This field is shared by all threads and must be in the shared area
instead, because where it's placed, it slows down access to other
fields of the struct by false sharing. Just moving this field gives
a steady 2% gain on the request rate (1.93 to 1.96 Mrps) on a 64-core
EPYC.
2026-01-28 16:07:27 +00:00
William Lallemand
6995fe60c3 MINOR: ssl: allow to disable certificate compression
This option allows to disable the certificate compression (RFC 8879)
using OpenSSL >= 3.2.0.

This feature is known to permit some denial of services by causing extra
memory allocations of approximately 22MiB and extra CPU work per
connection with OpenSSL versions affected by CVE-2025-66199.
( https://openssl-library.org/news/vulnerabilities/index.html#CVE-2025-66199 )

Setting this to "off" permits to mitigate the problem.

Must be backported to every stable branches.
2026-01-27 16:10:41 +01:00
Remi Tricot-Le Breton
9b1faee4c9 BUG/MINOR: ssl: Encrypted keys could not be loaded when given alongside certificate
The SSL passphrase callback function was only called when loading
private keys from a dedicated file (separate from the corresponding
certificate) but not when both the certificate and the key were in the
same file.
We can now load them properly, regardless of how they are provided.
A flas had to be added in the 'passphrase_cb_data' structure because in
the 'ssl_sock_load_pem_into_ckch' function, when calling
'PEM_read_bio_PrivateKey' there might be no private key in the PEM file
which would mean that the callback never gets called (and cannot set the
'passphrase_idx' to -1).

This patch can be backported to 3.3.
2026-01-26 14:09:13 +01:00
Willy Tarreau
1a3252e956 MEDIUM: pools: better check for size rounding overflow on registration
Certain object sizes cannot be controlled at declaration time because
the resulting object size may be slightly extended (tag, caller),
aligned and rounded up, or even doubled depending on pool settings
(e.g. if backup is used).

This patch addresses this by enlarging the type in the pool registration
to 64-bit so that no info is lost from the declaration, and extra checks
for overflows can be performed during registration after various rounding
steps. This allows to catch issues such as these ones and to report a
suitable error:

  global
      tune.http.logurilen 2147483647

  frontend
      capture request header name len 2147483647
      http-request capture src len 2147483647
      tcp-request content capture src len 2147483647
2026-01-26 11:54:14 +01:00
Amaury Denoyelle
b52c60d366 MEDIUM: proxy: implement persistent named defaults
This patch changes the handling of named defaults sections. Prior to
this patch, every unreferenced defaults proxies were removed on post
parsing. Now by default, these sections are kept after postparsing and
only purged on deinit. The objective is to allow reusing them as base
configuration for dynamic backends.

To implement this, refcount of every still addressable named sections is
incremented by one after parsing. This ensures that they won't be
removed even if referencing proxies are removed at runtime. This is done
via the new function proxy_ref_all_defaults().

To ensure defaults instances are still properly removed on deinit, the
inverse operation is performed : refcount is decremented by one on every
defaults sections via proxy_unref_all_defaults().

The original behavior can still be used by using the new global keyword
tune.defaults.purge. This is useful for users using configuration with
large number of defaults and not interested in dynamic backends
creation.
2026-01-22 18:06:42 +01:00
Amaury Denoyelle
116983ad94 MEDIUM: cfgparse: do not store unnamed defaults in name tree
Defaults section are indexed by their name in defproxy_by_name tree. For
named sections, there is no duplicate : if two instances have the same
name, the older one is removed from the tree. However, this was not the
case for unnamed defaults which are all stored inconditionnally in
defproxy_by_name.

This commit introduces a new approach for unnamed defaults. Now, these
instances are never inserted in the defproxy_by_name tree. Indeed, this
is not needed as no tree lookup is performed with empty names. This may
optimize slightly config parsing with a huge number of named and unnamed
defaults sections, as the first ones won't fill up the tree needlessly.

However, defproxy_by_name tree is also used to purge unreferenced
defaults instances, both on postparsing and deinit. Thus, a new approach
is needed for unnamed sections cleanup. Now, each time a new defaults is
parsed, if the previous instance is unnamed, it is freed unless if
referenced by a proxy. When config parsing is ended, a similar operation
is performed to ensure the last unnamed defaults section won't stay in
memory. To implement this, last_defproxy static variable is now set to
global. Unnamed sections which cannot be removed due to proxies
referencing proxies will still be removed when such proxies are freed
themselves, at runtime or on deinit.
2026-01-22 17:57:16 +01:00
Amaury Denoyelle
848e0cd052 MINOR: proxy: simplify defaults proxies list storage
Defaults proxies instance are stored in a global name tree. When there
is a name conflict and the older entry cannot be simply discarded as it
is already referenced, the older entry is instead removed from the name
tree and inserted into the orphaned list.

The purpose of the orphaned list was to guarantee that any remaining
unreferenced defaults are purged either on postparsing or deinit.
However, this is in fact completely useless. Indeed on postparsing,
orphaned entries are always referenced. On deinit instead, defaults are
already freed along the cleanup of all frontend/backend instances clean
up, thanks to their refcounting.

This patch streamlines this by removing orphaned list. Instead, a
defaults section is inserted into a new global defaults_list during
their whole lifetime. This is not strictly necessary but it ensures that
defaults instances can still be accessed easily in the future if needed
even if not present in the name tree. On deinit, a BUG_ON() is added to
ensure that defaults_list is indeed emptied.

Another benefit from this patch is to simplify the defaults deletion
procedure. Orphaned simple list is replaced by a proper double linked
list implementation, so a single LIST_DELETE() is now performed. This
will be notably useful as defaults may be removed at runtime in the
future if backends deletion at runtime is implemented.
2026-01-22 17:57:09 +01:00