]> git.ipfire.org Git - thirdparty/haproxy.git/log
thirdparty/haproxy.git
4 months agoBUG/MEDIUM: uxst: fix outgoing abns address family in connect()
Willy Tarreau [Fri, 21 Feb 2025 06:53:21 +0000 (07:53 +0100)] 
BUG/MEDIUM: uxst: fix outgoing abns address family in connect()

Since we reworked the unix socket families in order to support custom
addresses for different addressing schemes, we've been using extra
values for the ss_family field in sockaddr_storage. These ones have
to be adjusted before calling bind() or connect(). It turns out that
after the abns/abnsz updates in 3.1, the connect() code was not adjusted
to take care of the change, resulting in AF_CUST_ABNS or AF_CUST_ABNSZ
to be placed in the address that was passed to connect().

The right approach is to locally copy the address, get its length,
fixup the family and use the fixed value and length for connect().

This must be backported to 3.1. Many thanks for @Mewp for reporting
this issue in github issue #2875.

4 months agoBUG/MINOR: cfgparse: fix NULL ptr dereference in cfg_parse_peers
Valentine Krasnobaeva [Thu, 20 Feb 2025 14:00:38 +0000 (15:00 +0100)] 
BUG/MINOR: cfgparse: fix NULL ptr dereference in cfg_parse_peers

When "peers" keyword is followed by more than one argument and it's the first
"peers" section in the config, cfg_parse_peers() detects it and exits with
"ERR_ALERT|ERR_FATAL" err_code.

So, upper layer parser, parse_cfg(), continues and parses the next keyword
"peer" and then he tries to check the global cfg_peers, which should contain
"my_cluster". The global cfg_peers is still NULL, because after alerting a user
in alertif_too_many_args, cfg_parse_peers() exited.

peers my_cluster __some_wrong_data__
peer haproxy1 1.1.1.1 1000

In order to fix this, let's add ERR_ABORT, if "peers" keyword is followed by
more than one argument. Like this parse_cfg() will stops immediately and
terminates haproxy with "too many args for peers my_cluster..." alert message.

It's more reliable, than add checks "if (cfg_peers !=NULL)" in "peer"
subparser, as we may have many "peers" sections.

peers my_another_cluster
peer haproxy1 1.1.1.2 1000

peers my_cluster  __some_wrong_data__
peer haproxy1 1.1.1.1 1000

In addition, for the example above, parse_cfg() will parse all configuration
until the end and only then terminates haproxy with the alert
"too many args...". Peer haproxy1 will be wrongly associated with
my_another_cluster.

This fixes the issue #2872.
This should be backported in all stable versions.

4 months agoBUG/MEDIUM: spoe/mux-spop: Introduce an NOOP action to deal with empty ACK
Christopher Faulet [Thu, 20 Feb 2025 10:56:24 +0000 (11:56 +0100)] 
BUG/MEDIUM: spoe/mux-spop: Introduce an NOOP action to deal with empty ACK

In the SPOP protocol, ACK frame with empty payload are allowed. However, in
that case, because only the payload is transferred, there is no data to
return to the SPOE applet. Only the end of input is reported. Thus the
applet is never woken up. It means that the SPOE filter will be blocked
during the processing timeout and will finally return an error.

To workaournd this issue, a NOOP action is introduced with the value 0. It
is only an internal action for now. It does not exist in the SPOP
protocol. When an ACK frame with an empy payload is received, this noop
action is transferred to the SPOE applet, instead of nothing. Thanks to this
trick, the applet is properly notified. This works because unknown actions
are ignored by the SPOE filter.

This patch must be backported to 3.1.

4 months agoBUG/MEDIUM: applet: Don't handle EOI/EOS/ERROR is applet is waiting for room
Christopher Faulet [Thu, 20 Feb 2025 09:00:31 +0000 (10:00 +0100)] 
BUG/MEDIUM: applet: Don't handle EOI/EOS/ERROR is applet is waiting for room

The commit 7214dcd52 ("BUG/MEDIUM: applet: Don't pretend to have more data
to handle EOI/EOS/ERROR") introduced a regression. Because of this patch, it
was possible to handle EOI/EOS/ERROR applet flags too early while the applet
was waiting for more room to transfer the last output data.

This bug can be encountered with any applet using its own buffers (cache and
stats for instance). And depending on the configuration and the timing, the
data may be truncated or the stream may be blocked, infinitely or not.
Streams blocked infinitely were observed with the cache applet and the HTTP
compression enabled.

For the record, it is important to detect EOI/EOS/ERROR applet flags to be
able to report the corresponding event on the SE and by transitivity on the
SC. Most of time, this happens when some data should be transferred to the
stream. The .rcv_buf callback function is called and these flags are
properly handled. However, some applets may also report them spontaneously,
outside of any data transfer. In that case, the .rcv_buf callback is not
called.

It is the purpose of this patch (and the one above). Being able to detect
pending EOI/EOS/ERROR applet flags. However, we must be sure to not handle
them too early at this place. When these flags are set, it means no more
data will be produced by the applet. So we must only wait to have
transferred everything to the stream. And this happens when the applet is no
longer waiting for more room.

This patch must be backported to 3.1 with the one above.

4 months ago[RELEASE] Released version 3.2-dev6 v3.2-dev6
Willy Tarreau [Wed, 19 Feb 2025 17:39:51 +0000 (18:39 +0100)] 
[RELEASE] Released version 3.2-dev6

Released version 3.2-dev6 with the following main changes :
    - BUG/MEDIUM: debug: close a possible race between thread dump and panic()
    - DEBUG: thread: report the spin lock counters as seek locks
    - DEBUG: thread: make lock time computation more consistent
    - DEBUG: thread: report the wait time buckets for lock classes
    - DEBUG: thread: don't keep the redundant _locked counter
    - DEBUG: thread: make lock_stat per operation instead of for all operations
    - DEBUG: thread: reduce the struct lock_stat to store only 30 buckets
    - MINOR: lbprm: add a new callback ->server_requeue to the lbprm
    - MEDIUM: server: allocate a tasklet for asyncronous requeuing
    - MAJOR: leastconn: postpone the server's repositioning under contention
    - BUG/MINOR: quic: reserve length field for long header encoding
    - BUG/MINOR: quic: fix CRYPTO payload size calcul for encoding
    - MINOR: quic: simplify length calculation for STREAM/CRYPTO frames
    - BUG/MINOR: mworker: section ignored in discovery after a post_section_parser
    - BUG/MINOR: mworker: post_section_parser for the last section in discovery
    - CLEANUP: mworker: "program" section does not have a post_section_parser anymore
    - MEDIUM: initcall: allow to register mutiple post_section_parser per section
    - CI: cirrus-ci: bump FreeBSD image to 14-2
    - DOC: initcall: name correctly REGISTER_CONFIG_POST_SECTION()
    - REGTESTS: stop using truncated.vtc on freebsd
    - MINOR: quic: refactor STREAM encoding and splitting
    - MINOR: quic: refactor CRYPTO encoding and splitting
    - BUG/MEDIUM: fd: mark FD transferred to another process as FD_CLONED
    - BUG/MINOR: ssl/cli: "show ssl crt-list" lacks client-sigals
    - BUG/MINOR: ssl/cli: "show ssl crt-list" lacks sigals
    - MINOR: ssl/cli: display more filenames in 'show ssl cert'
    - DOC: watchdog: document the sequence of the watchdog and panic
    - MINOR: ssl: store the filenames resulting from a lookup in ckch_conf
    - MINOR: startup: allow hap_register_feature() to enable a feature in the list
    - MINOR: quic: support frame type as a varint
    - BUG/MINOR: startup: leave at first post_section_parser which fails
    - BUG/MINOR: startup: hap_register_feature() fix for partial feature name
    - BUG/MEDIUM: cli: Be sure to drop all input data in END state
    - BUG/MINOR: cli: Wait for the last ACK when FDs are xferred from the old worker
    - BUG/MEDIUM: filters: Handle filters registered on data with no payload callback
    - BUG/MINOR: fcgi: Don't set the status to 302 if it is already set
    - MINOR: ssl/crtlist: split the ckch_conf loading from the crtlist line parsing
    - MINOR: ssl/crtlist: handle crt_path == cc->crt in crtlist_load_crt()
    - MINOR: ssl/ckch: return from ckch_conf_clean() when conf is NULL
    - MEDIUM: ssl/crtlist: "crt" keyword in frontend
    - DOC: configuration: document the "crt" frontend keyword
    - DEV: h2: add a Lua-based HTTP/2 connection tracer
    - BUG/MINOR: quic: prevent crash on conn access after MUX init failure
    - BUG/MINOR: mux-quic: prevent crash after MUX init failure
    - DEV: h2: fix flags for the continuation frame
    - REGTESTS: Fix truncated.vtc to send 0-CRLF
    - BUG/MINOR: mux-h2: Properly handle full or truncated HTX messages on shut
    - Revert "REGTESTS: stop using truncated.vtc on freebsd"
    - MINOR: mux-quic: define a QCC application state member
    - MINOR: mux-quic/h3: emit SETTINGS via MUX tasklet handler
    - MINOR: mux-quic/h3: support temporary blocking on control stream sending

4 months agoMINOR: mux-quic/h3: support temporary blocking on control stream sending
Amaury Denoyelle [Tue, 18 Feb 2025 15:14:19 +0000 (16:14 +0100)] 
MINOR: mux-quic/h3: support temporary blocking on control stream sending

When HTTP/3 layer is initialized via QUIC MUX, it first emits a SETTINGS
frame on an unidirectional control stream. However, this could be
prevented if client did not provide initial flow control.

Previously, QUIC MUX was unable to deal with such situation. Thus, the
connection was closed immediately and no transfer could occur. Improve
this by extending QUIC MUX application layer API : initialization may
now return a transient error. This allows MUX to continue to use the
connection normally. Initialization will be retried periodically alter
until it can succeed.

This new API allows to deal with the flow control issue described above.
Note that this patch is not considered as a bug fix. Indeed, clients are
strongly advised to provide enough flow control for a SETTINGS frame
exchange.

4 months agoMINOR: mux-quic/h3: emit SETTINGS via MUX tasklet handler
Amaury Denoyelle [Mon, 17 Feb 2025 15:00:03 +0000 (16:00 +0100)] 
MINOR: mux-quic/h3: emit SETTINGS via MUX tasklet handler

Previously, QUIC MUX application layer was installed and initialized via
MUX init. However, the latter stage involve I/O operations, for example
when using HTTP/3 with the emission of a SETTINGS frame.

Change this to prevent any I/O operations during MUX init. As such,
finalize app_ops callback is now called during the first invokation of
qcc_io_send(), in the context of MUX tasklet. To implement this, a new
application state value is added, to detect the transition from NULL to
INIT stage.

4 months agoMINOR: mux-quic: define a QCC application state member
Amaury Denoyelle [Tue, 18 Feb 2025 10:42:59 +0000 (11:42 +0100)] 
MINOR: mux-quic: define a QCC application state member

Introduce a new QCC field to track the current application layer state.
For the moment, only INIT and SHUT state are defined. This allows to
replace the older flag QC_CF_APP_SHUT.

This commit does not bring major changes. It is only necessary to permit
future evolutions on QUIC MUX. The only noticeable change is that QMUX
traces can now display this new field.

4 months agoRevert "REGTESTS: stop using truncated.vtc on freebsd"
Christopher Faulet [Tue, 18 Feb 2025 06:34:12 +0000 (07:34 +0100)] 
Revert "REGTESTS: stop using truncated.vtc on freebsd"

This reverts commit 0b9a75e8781593c250f6366a64a019018ade688e.

Thanks to the previous fixes ("REGTESTS: Fix truncated.vtc to send 0-CRLF" and
"BUG/MINOR: mux-h2: Properly handle full or truncated HTX messages on shut"),
this script can be reenabled for FreeBSD.

4 months agoBUG/MINOR: mux-h2: Properly handle full or truncated HTX messages on shut
Christopher Faulet [Mon, 17 Feb 2025 17:48:17 +0000 (18:48 +0100)] 
BUG/MINOR: mux-h2: Properly handle full or truncated HTX messages on shut

On shut, truncated HTX messages were not properly handled by the H2
multiplexer. Depending on how data were emitted, a chunked HTX message
without the 0-CRLF could be considered as full and an empty data with ES
flag set could be emitted instead of a RST_STREAM(CANCEL) frame.

In the H2 multiplexer, when a shut is performed, an HTX message is
considered as truncated if more HTX data are still expected. It is based on
the presence or not of the H2_SF_MORE_HTX_DATA flag on the H2 stream.
However, this flag is set or unset depending on the HTX extra field
value. This field is used to state how much data that must still be
transferred, based on the announced data length. For a message with a
content-length, this assumption is valid. But for a chunked message, it is
not true. Only the length of the current chunk is announced. So we cannot
rely on this field in that case to know if a message is full or not.

Instead, we must rely on the HTX start-line flags to know if more HTX data
are expected or not. If the xfer length is known (the HTX_SL_F_XFER_LEN flag
is set on the HTX start-line), it means that more data are always expected,
until the end of message is reached (the HTX_FL_EOM flag is set on the HTX
message). This is true for bodyless message because the end of message is
reported with the end of headers. This is also true for tunneled messages
because the end of message is received before switching the H2 stream in
tunnel mode.

This patch must be backported as far as 2.8.

4 months agoREGTESTS: Fix truncated.vtc to send 0-CRLF
Christopher Faulet [Tue, 18 Feb 2025 06:31:51 +0000 (07:31 +0100)] 
REGTESTS: Fix truncated.vtc to send 0-CRLF

When a chunked messages is sent, the 0-CRLF must be explicitely sent. Since
the begining, it is missing. Just add it.

4 months agoDEV: h2: fix flags for the continuation frame
Willy Tarreau [Tue, 18 Feb 2025 13:16:28 +0000 (14:16 +0100)] 
DEV: h2: fix flags for the continuation frame

It's flag 2 (end of headers) that's defined there, not 3 (padded).

4 months agoBUG/MINOR: mux-quic: prevent crash after MUX init failure
Amaury Denoyelle [Mon, 17 Feb 2025 09:54:41 +0000 (10:54 +0100)] 
BUG/MINOR: mux-quic: prevent crash after MUX init failure

qmux_init() may fail for several reasons. In this case, connection
resources are freed and underlying and a CONNECTION_CLOSE will be
emitted via its quic_conn instance.

In case of qmux_init() failure, qcc_release() is used to clean up
resources, but QCC <conn> member is first resetted to NULL, as
connection released must be delayed. Some cleanup operations are thus
skipped, one of them is the resetting of <ctx> connection member to
NULL. This may cause a crash as <ctx> is a dangling pointer after QCC
release. One of the possible reproducer is to activate QMUX traces,
which will cause a segfault on the qmux_init() error leave trace.

To fix this, simply reset <ctx> to NULL manually on qmux_init() failure.

This must be backported up to 3.0.

4 months agoBUG/MINOR: quic: prevent crash on conn access after MUX init failure
Amaury Denoyelle [Mon, 17 Feb 2025 16:15:49 +0000 (17:15 +0100)] 
BUG/MINOR: quic: prevent crash on conn access after MUX init failure

Initially, QUIC-MUX was responsible to reset quic_conn <conn> member to
NULL when MUX was released. This was performed via qcc_release().

However, qcc_release() is also used on qmux_init() failure. In this
case, connection must be freed via its session, so QCC <conn> member is
resetted to NULL prior to qcc_release(), which prevents quic_conn <conn>
member to also be resetted. As the connection is freed soon after,
quic_conn <conn> is a dangling pointer, which may cause crashes.

This bug should be very rare as first it implies that QUIC-MUX
initialization has failed (for example due to a memory alloc error).
Also, <conn> member is rarely used by quic_conn instance. In fact, the
only reproducible crash was done with QUIC traces activated, as in this
case connection is accessed via quic_conn under __trace_enabled()
function.

To fix this, detach connection from quic_conn via the XPRT layer instead
of the MUX. More precisely, this is performed via quic_close(). This
should ensure that it will always be conducted, either on normal
connection closure, but also after special conditions such as MUX init
failure.

This should be backported up to 2.6.

4 months agoDEV: h2: add a Lua-based HTTP/2 connection tracer
Willy Tarreau [Tue, 18 Feb 2025 07:40:39 +0000 (08:40 +0100)] 
DEV: h2: add a Lua-based HTTP/2 connection tracer

The following config is sufficient to trace H2 exchanges between a client
and a server:

   global
       lua-load "dev/h2/h2-tracer.lua"

   listen h2_sniffer
       mode tcp
       bind :8002
       filter lua.h2-tracer #hex
       server s1 127.0.0.1:8003

The commented "hex" argument will also display full frames in hex (not
recommended). The connections are prefixed with a 3-hex digit number in
order to also support a bit of multiplexing without impacting the reading
too much. The screen is split in two, with the request on the left and
the response on the right. Here's an example of what it does between an
haproxy backend and an haproxy frontend both in H2, when submitted a
curl request for /?s=30k handled by httpterm:

  [001] ### req start
  [001] [PREFACE len=24]
  [001] [SETTINGS sid=0 len=24 (bytes=24)]
  [001]                                          | ### res start
  [001]                                          | [SETTINGS sid=0 len=18 (bytes=27)]
  [001]                                          | [SETTINGS ACK sid=0 len=0 (bytes=0)]
  [001] [SETTINGS ACK sid=0 len=0 (bytes=56)]
  [001] [HEADERS EH+ES sid=1 len=47 (bytes=47)]
  [001]                                          | [HEADERS EH sid=1 len=101 (bytes=15351)]
  [001]                                          | [DATA sid=1 len=15126 (bytes=15241)]
  [001]                                          | [DATA sid=1 len=1258 (bytes=106)]
  [001]                                          |                  ... -106 = 1152
  [001]                                          |                    ... -1152 = 0
  [001] [WINDOW_UPDATE sid=1 len=4 (bytes=43)]
  [001] [WINDOW_UPDATE sid=0 len=4 (bytes=30)]
  [001] [WINDOW_UPDATE sid=1 len=4 (bytes=17)]
  [001] [WINDOW_UPDATE sid=0 len=4 (bytes=4)]
  [001]                                          | [DATA ES sid=1 len=14336 (bytes=14336)]
  [001] [WINDOW_UPDATE sid=0 len=4 (bytes=4)]
  [001] ### req end: 31080 bytes total
  [001]                                          | [GOAWAY sid=0 len=8 (bytes=8)]
  [001]                                          | ### res end: 31097 bytes total

It deserves some improvements. For instance at the moment it does not
verify the preface, any 24 bytes will work. It does not perform any
protocol validation either. Detecting some issues such as out-of-sequence
frames could be helpful. But it already helps as-is.

4 months agoDOC: configuration: document the "crt" frontend keyword
William Lallemand [Mon, 17 Feb 2025 16:06:59 +0000 (17:06 +0100)] 
DOC: configuration: document the "crt" frontend keyword

Document the "crt" keyword of frontend and listen section.

4 months agoMEDIUM: ssl/crtlist: "crt" keyword in frontend
William Lallemand [Tue, 11 Feb 2025 14:15:06 +0000 (15:15 +0100)] 
MEDIUM: ssl/crtlist: "crt" keyword in frontend

This patch implements the "crt" keywords in frontend, declaring an
implicit crt-list named after the frontend.

The patch is split in two steps:

The first step is the crt keyword parser, which parses crt lines and
fill a "cfg_crt_node" struct containing a ssl_bind_conf and a
ckch_conf which are put in a list to be used later.

After parsing the frontend section, as a 2nd step, a
post_section_parser is called, it will create a crt-list named after
the frontend and will fill it with certificates from the list of
cfg_crt_node. Once created this crt-list will be loaded in every "ssl"
bind lines that didn't declare any crt or crt-list.

Example:

    listen https
       bind :443 ssl
       crt foobar.pem
       crt test1.net.crt key test1.net.key

Implements part of #2854

4 months agoMINOR: ssl/ckch: return from ckch_conf_clean() when conf is NULL
William Lallemand [Wed, 12 Feb 2025 13:48:38 +0000 (14:48 +0100)] 
MINOR: ssl/ckch: return from ckch_conf_clean() when conf is NULL

ckch_conf_clean() mustn't be executed when the argument is NULL, this
will keep the API more consistant like any free() function.

4 months agoMINOR: ssl/crtlist: handle crt_path == cc->crt in crtlist_load_crt()
William Lallemand [Wed, 12 Feb 2025 14:29:39 +0000 (15:29 +0100)] 
MINOR: ssl/crtlist: handle crt_path == cc->crt in crtlist_load_crt()

Handle the case where crt_path == cc->crt, so the pointer doesn't get
free'd before getting strdup'ed in crtlist_load_crt().

4 months agoMINOR: ssl/crtlist: split the ckch_conf loading from the crtlist line parsing
William Lallemand [Tue, 11 Feb 2025 14:01:33 +0000 (15:01 +0100)] 
MINOR: ssl/crtlist: split the ckch_conf loading from the crtlist line parsing

ckch_conf loading is not that simple as it requires to check
- if the cert already exists in the ckchs_tree
- if the ckch_conf is compatible with an existing cert in ckchs_tree
- if the cert is a bundle which need to load multiple ckch_store

This logic could be reuse elsewhere, so this commit introduce the new
crtlist_load_crt() function which does that.

4 months agoBUG/MINOR: fcgi: Don't set the status to 302 if it is already set
Christopher Faulet [Mon, 17 Feb 2025 15:37:47 +0000 (16:37 +0100)] 
BUG/MINOR: fcgi: Don't set the status to 302 if it is already set

When a "Location" header was found in a FCGI response, the status code was
forced to 302. But it should only be performed if no status code was set
first.

So now, we take care to not override an already defined status code when the
"Location" header is found.

This patch should fix the issue #2865. It must backported to all stable
versions.

4 months agoBUG/MEDIUM: filters: Handle filters registered on data with no payload callback
Christopher Faulet [Mon, 17 Feb 2025 14:54:49 +0000 (15:54 +0100)] 
BUG/MEDIUM: filters: Handle filters registered on data with no payload callback

An HTTP filter with no http_payload callback function may be registered on
data. In that case, this filter is obviously not called when some data are
received but it remains important to update its internal state to be sure to
keep it synchronized on the stream, especially its offet value. Otherwise,
the wrong calculation on the global offset may be performed in
flt_http_end(), leading to an integer overflow when data are moved from
input to output. This overflow triggers a BUG_ON() in c_adv().

The same is true for TCP filters with no tcp_payload callback function.

This patch must be backport to all stable versions.

4 months agoBUG/MINOR: cli: Wait for the last ACK when FDs are xferred from the old worker
Christopher Faulet [Mon, 17 Feb 2025 14:16:15 +0000 (15:16 +0100)] 
BUG/MINOR: cli: Wait for the last ACK when FDs are xferred from the old worker

On reload, the new worker requests bound FDs to the old one. The old worker
sends them in message of at most 252 FDs. Each message is acknowledged by
the new worker. All messages sent or received by the old worker are handled
manually via sendmsg/recv syscalls. So the old worker must be sure consume
all the ACK replies. However, the last one was never consumed. So it was
considered as a command by the CLI applet. This issue was hidden since
recently. But it was the root cause of the issue #2862.

Note this last ack is also the first one when there are less than 252 FDs to
transfer.

This patch must be backported to all stable versions.

4 months agoBUG/MEDIUM: cli: Be sure to drop all input data in END state
Christopher Faulet [Mon, 17 Feb 2025 13:49:34 +0000 (14:49 +0100)] 
BUG/MEDIUM: cli: Be sure to drop all input data in END state

Commit 7214dcd ("BUG/MEDIUM: applet: Don't pretend to have more data to
handle EOI/EOS/ERROR") revealed a bug with the CLI applet. Pending input
data when the applet is in CLI_ST_END state were never consumed or dropped,
leading to a wakeup loop.

The CLI applet implements its own snd_buf callback function. It is important
it consumes all pending input data. Otherwise, the applet is woken up in
loop until it empties the request buffer. Another way to fix the issue would
be to report an error. But in that case, it seems reasonnable to drop these
data.

The issue can be observed on reload, in master/worker mode, because of issue
about the last ACK message which was never consummed by the _getsocks()
command.

This patch should fix the issue #2862. It must be backported to 3.1 with the
commit above.

4 months agoBUG/MINOR: startup: hap_register_feature() fix for partial feature name
William Lallemand [Mon, 17 Feb 2025 13:38:49 +0000 (14:38 +0100)] 
BUG/MINOR: startup: hap_register_feature() fix for partial feature name

In patch 2fe4cbd8e ("MINOR: startup: allow hap_register_feature() to
enable a feature in the list"), the ability to overwrite a '-' in the
feature list was added. However the code was not tokenizing correctly
the string, and partial feature name found in the name could result in
having the same feature name multiple time.

This patch rewrites the lookup of the string by tokenizing it correctly.

4 months agoBUG/MINOR: startup: leave at first post_section_parser which fails
William Lallemand [Mon, 17 Feb 2025 09:59:46 +0000 (10:59 +0100)] 
BUG/MINOR: startup: leave at first post_section_parser which fails

Since we are now iterating on post_section_parser() for a same keyword,
we need to exit at the first ERR_ABORT.

The post_section_parser() is called when parsing a new section, but also
at the end of the file to be called for the last section.

The changes in 4de86bb ("MEDIUM: initcall: allow to register mutiple
post_section_parser per section") should have added tests on the
ERR_ABORT value.

Also pcs->post_section_parser() must be called instead of
cs->post_section_parser() because we could have a NULL ptr.

This bug does not affect anything since we don't use
REGISTER_CONFIG_POST_SECTION() yet.

4 months agoMINOR: quic: support frame type as a varint
Amaury Denoyelle [Wed, 12 Feb 2025 16:49:09 +0000 (17:49 +0100)] 
MINOR: quic: support frame type as a varint

QUIC frame type is encoded as a variable-length integer. Thus, 64-bit
integer should be used for them. Currently, this was not the case as
type was represented as a 1-byte char inside quic_frame structure. This
does not cause any issue with QUIC from RFC9000, as all frame types fit
in this range. Furthermore, a QUIC implementation is required to use the
smallest size varint when encoding a frame type.

However, the current code is unable to accept QUIC extension with bigger
frame types. This is notably the case for quic-on-streams draft. Thus,
this commit readjusts quic_frame architecture to be able to support
higher frame type values.

First, type field of quic_frame is changed to a 64-bits variable. Both
encoding and decoding frame functions uses variable-length integer
helpers to manipulate the frame type field.

Secondly, the quic_frame builders/parsers infrastructure is still
preserved. However, it could be impossible to define new large frame
type as an index into quic_frame_builders / quic_frame_parsers arrays.
Thus, wrapper functions are now provided to access the builders and
parsers. Both qf_builder() and qf_parser() wrappers can then be extended
to return custom builder/parser instances for larger frame type.

Finally, unknown frame type detection also uses the new wrapper
quic_frame_is_known(). As with builders/parsers, for large frame type,
this function must be manually completed to support a new type value.

4 months agoMINOR: startup: allow hap_register_feature() to enable a feature in the list
William Lallemand [Thu, 13 Feb 2025 23:00:28 +0000 (00:00 +0100)] 
MINOR: startup: allow hap_register_feature() to enable a feature in the list

This patch allows hap_register_feature() to enable a feature in the list
which was already registered and marked disabled.

This way we could enable automatically some features under certain
condition without the need of the USE argument with make and correctly
report its activation.

4 months agoMINOR: ssl: store the filenames resulting from a lookup in ckch_conf
William Lallemand [Thu, 13 Feb 2025 16:35:10 +0000 (17:35 +0100)] 
MINOR: ssl: store the filenames resulting from a lookup in ckch_conf

With this patch, files resulting from a lookup (*.key, *.ocsp,
*.issuer etc) are now stored in the ckch_conf.

It allows to see the original filename from where it was loaded in "show
ssl cert <filename>"

4 months agoDOC: watchdog: document the sequence of the watchdog and panic
Willy Tarreau [Thu, 13 Feb 2025 15:40:17 +0000 (16:40 +0100)] 
DOC: watchdog: document the sequence of the watchdog and panic

Each time we go into the watchdog and panic code, it's super hard to
figure who calls what since signals are involved to bounce between
threads. Let's document the main principles and sequences to ease the
journey next time.

4 months agoMINOR: ssl/cli: display more filenames in 'show ssl cert'
William Lallemand [Thu, 13 Feb 2025 15:18:02 +0000 (16:18 +0100)] 
MINOR: ssl/cli: display more filenames in 'show ssl cert'

"show ssl cert <file>" only displays a unique filename, which is the
key used in the ckch_store tree. This patch extends it by displaying
every filenames from the ckch_conf that can be configured with the
crt-store.

In order to be more consistant, some changes are needed in the future:
- we need to store the complete path in the ckch_conf (meaning with
  crt-path or key-path)
- we need to fill a ckch_conf in cases the files are autodiscovered

4 months agoBUG/MINOR: ssl/cli: "show ssl crt-list" lacks sigals
William Lallemand [Wed, 12 Feb 2025 16:13:03 +0000 (17:13 +0100)] 
BUG/MINOR: ssl/cli: "show ssl crt-list" lacks sigals

1d3c8223 ("MINOR: ssl: allow to change the server signature algorithm")
mplemented the sigals keyword in the crt-list but never the dump of the
keyword over the CLI.

Must be backported as far as 2.8.

4 months agoBUG/MINOR: ssl/cli: "show ssl crt-list" lacks client-sigals
William Lallemand [Wed, 12 Feb 2025 16:09:21 +0000 (17:09 +0100)] 
BUG/MINOR: ssl/cli: "show ssl crt-list" lacks client-sigals

b6ae2aafde43 ("MINOR: ssl: allow to change the signature algorithm for
client authentication") implemented the client-sigals keyword in the
crt-list but never the dump of the keyword over the CLI.

Must be backported as far as 2.8.

4 months agoBUG/MEDIUM: fd: mark FD transferred to another process as FD_CLONED
Willy Tarreau [Wed, 12 Feb 2025 15:25:13 +0000 (16:25 +0100)] 
BUG/MEDIUM: fd: mark FD transferred to another process as FD_CLONED

The crappy epoll API stroke again with reloads and transferred FDs.
Indeed, when listening sockets are retrieved by a new worker from a
previous one, and the old one finally stops listening on them, it
closes the FDs. But in this case, since the sockets themselves were
not closed, epoll will not unregister them and will continue to
report new activity for these in the old process, which can only
observe, count an fd_poll_drop event and not unregister them since
they're not reachable anymore.

The unfortunate effect is that long-lasting old processes are woken
up at the same rate as the new process when accepting new connections,
and can waste a lot of CPU. Accept rates divided by 8 were observed on
a small test involving a slow transfer on 10 connections facing a reload
every second so that 10 processes were busy dealing with them while
another process was hammering the service with new connections.

Fortunately, years ago we implemented a flag FD_CLONED exactly for
similar purposes. Let's simply mark transferred FDs with FD_CLONED
so that the process knows that these ones require special treatment
and have to be manually unregistered before being closed. This does
the job fine, now old processes correctly unregister the FD before
closing it and no longer receive accept events for the new process.

This needs to be backported to all stable versions. It only affects
epoll, as usual, and this time in combination with transferred FDs
(typically reloads in master-worker mode). Thanks to Damien Claisse
for providing all detailed measurements and statistics allowing to
understand and reproduce the problem.

4 months agoMINOR: quic: refactor CRYPTO encoding and splitting
Amaury Denoyelle [Mon, 10 Feb 2025 10:28:35 +0000 (11:28 +0100)] 
MINOR: quic: refactor CRYPTO encoding and splitting

This patch is the direct follow-up of the previous one which refactor
STREAM frame encoding. Reuse the newly defined quic_strm_frm_fillbuf()
and quic_strm_frm_split() functions for CRYPTO frame encoding.

The code for CRYPTO and STREAM frames encoding should now be clearer as
it is mostly identical.

4 months agoMINOR: quic: refactor STREAM encoding and splitting
Amaury Denoyelle [Fri, 7 Feb 2025 17:48:18 +0000 (18:48 +0100)] 
MINOR: quic: refactor STREAM encoding and splitting

CRYPTO and STREAM frames encoding is similar. If payload is too large,
frame will be splitted and only the first payload part will be written
in the output QUIC packet. This process is complexified by the presence
of a variable-length integer Length field prior to the payload.

This commit aims at refactor these operations. Define two functions to
simplify the code :
* quic_strm_frm_fillbuf() which is used to calculate the optimal frame
  length of a STREAM/CRYPTO frame with its payload in a buffer
* quic_strm_frm_split() which is used to split the frame payload if
  buffer is too small

With this patch, both functions are now implemented for STREAM encoding.

4 months agoREGTESTS: stop using truncated.vtc on freebsd
William Lallemand [Wed, 12 Feb 2025 12:30:50 +0000 (13:30 +0100)] 
REGTESTS: stop using truncated.vtc on freebsd

We never succeed to make the truncated.vtc reg-test work constantly on
the Cirrus FreeBSD CI.

Let's exclude it from the FreeBSD tests so the CI don't break randomly.

4 months agoDOC: initcall: name correctly REGISTER_CONFIG_POST_SECTION()
William Lallemand [Wed, 12 Feb 2025 12:27:44 +0000 (13:27 +0100)] 
DOC: initcall: name correctly REGISTER_CONFIG_POST_SECTION()

REGISTER_CONFIG_POST_SECTION() was not named correctly.

4 months agoCI: cirrus-ci: bump FreeBSD image to 14-2
William Lallemand [Wed, 12 Feb 2025 12:18:55 +0000 (13:18 +0100)] 
CI: cirrus-ci: bump FreeBSD image to 14-2

FreeBSD CI since to be broken for a while, try to upgrade the image to
the latest 14.2 version.

4 months agoMEDIUM: initcall: allow to register mutiple post_section_parser per section
William Lallemand [Mon, 10 Feb 2025 14:07:05 +0000 (15:07 +0100)] 
MEDIUM: initcall: allow to register mutiple post_section_parser per section

Before this patch, REGISTER_CONFIG_SECTION() allowed to register one and only
one callback (<post>) called after the parsing of a section.

It was limitating because you couldn't register a post callback from anywhere
else in the code.

This patch introduces the new REGISTER_CONFIG_SECTION_POST() macros which allows
to register a new post callback for a section keyword from anywhere.

This patch introduces the feature by allowing `struct cfg_section` entries that
does not have a `section_parser`, and then iterating on all cfg_section with a
post_section_parser for a keyword.

4 months agoCLEANUP: mworker: "program" section does not have a post_section_parser anymore
William Lallemand [Wed, 12 Feb 2025 11:29:48 +0000 (12:29 +0100)] 
CLEANUP: mworker: "program" section does not have a post_section_parser anymore

The "program" section does not have a post_section_parser anymore so no
need to make an exception for it.

4 months agoBUG/MINOR: mworker: post_section_parser for the last section in discovery
William Lallemand [Wed, 12 Feb 2025 11:31:11 +0000 (12:31 +0100)] 
BUG/MINOR: mworker: post_section_parser for the last section in discovery

Previous patch 2c270a05f ("BUG/MINOR: mworker: section ignored in
discovery after a post_section_parser") needs an adjustment for the last
section of the file.

Indeed the post_section_parser of the last section must not be called in
discovery mode.

Must be backported in 3.1.

4 months agoBUG/MINOR: mworker: section ignored in discovery after a post_section_parser
William Lallemand [Wed, 12 Feb 2025 11:09:05 +0000 (12:09 +0100)] 
BUG/MINOR: mworker: section ignored in discovery after a post_section_parser

When a new section is discovered, the post_section_parser of the
previous section is called. However in the new master-worker mode the
discovery mode will skip the post_section_parser. But instead of
trying to parse the current section keyword after that, it would skip
completely the current line.

This is a minor bug since there isn't a lot of section with
post_section_parser, and not a lot of section to parse in discovery
mode.

But this could be reproduced like this:

global
        expose-deprecated-directives

resolvers res
parse-resolv-conf

program foo
        command sleep 10

program bar
       command sleep 10

Ths 'resolvers' section has a post_section_parser which will be ignored
in discovery mode with the consequence of ignoring the first program
section.

This must be backported in 3.1.

4 months agoMINOR: quic: simplify length calculation for STREAM/CRYPTO frames
Amaury Denoyelle [Wed, 12 Feb 2025 09:55:51 +0000 (10:55 +0100)] 
MINOR: quic: simplify length calculation for STREAM/CRYPTO frames

STREAM and CRYPTO frames have a similar encoding format. In particular,
both of them have a variable-length integer Length field just before the
frame payload.

It is complex to determine the optimal Length value before copying the
payload data in the remaining buffer space. As such, helper functions
were implemented to calculate this. However, CRYPTO and STREAM frames
encoding implementation were not completely aligned, which renders the
code harder to follow.

The purpose of this commit is to simplify CRYPTO and STREAM frames
encoding. First, a new helper quic_int_cap_length() is defined which is
useful to determine the optimal buffer room available if prefixed by a
variable-length integer as Length field. Then, processing of both CRYPTO
and STREAM frames is now nearly identical, based on this new helper
function. Functions max_available_room() and max_stream_data_size() are
now unused and are removed.

4 months agoBUG/MINOR: quic: fix CRYPTO payload size calcul for encoding
Amaury Denoyelle [Tue, 11 Feb 2025 13:35:52 +0000 (14:35 +0100)] 
BUG/MINOR: quic: fix CRYPTO payload size calcul for encoding

Function max_stream_data_size() is used to determine the payload length
of a CRYPTO frame. It takes into account that the CRYPTO length field is
a variable length integer.

Implemented calcul was incorrect as it reserved too much space as a
frame header. This error is mostly due because max_stream_data_size()
reuses max_available_room() which also reserve space for a variable
length integer. This results in CRYPTO frames shorter of 1 to 2 bytes
than the maximum achievable value, which produces in the end datagram
shorter than the MTU.

Fix max_stream_data_size() implementation. It is now merely a wrapper on
max_available_room(). This ensures that CRYPTO frame encoding is now
properly optimized to use the MTU available.

This should be backported up to 2.6.

4 months agoBUG/MINOR: quic: reserve length field for long header encoding
Amaury Denoyelle [Tue, 11 Feb 2025 13:34:57 +0000 (14:34 +0100)] 
BUG/MINOR: quic: reserve length field for long header encoding

Long header packets have a mandatory Length field, which contains the
size of Packet number and payload, encoded as a variable-length integer.
Its value can thus only be determined after the payload size is known,
which depends on the remaining buffer space after this variable-length
field.

Packet payload are encoded in two steps. First, a list of input frames
is processed until the packet buffer is full. CRYPTO and STREAM frames
payload can be splitted if need to fill the buffer. Real encoding is
then performed as a second stage operation, first with Length field,
then with the selected frames themselves.

Before this patch, no space was reserved in the buffer for Length field
when attaching the frames to the packet. This could result in a error as
the packet payload would be too large for the remaining space.

In practice, this issue was rarely encounted, mostly as a side-effect
from another issue linked to CRYPTO frame encoding. Indeed, a wrong
calculation is performed on CRYPTO splitting, which results in frame
payload shorter by a few bytes than expected. This however ensured there
would be always enough room for the Length field and payload during
encoding. As CRYPTO frames are the only big enough content emitted with
a Long header packet, this renders the current issue mostly non
reproducible.

Fix the original issue by reserving some space for Length field prior to
frame payload calculation, using a maximum value based on the remaining
room space. Packet length is then reduced if needed when encoding is
performed, which ensures there is always enough room for the selected
frames.

Note that the other issue impacting CRYPTO frame encoding is not yet
fixed. This could result in datagrams with Long header packets not
completely extended to the full MTU. The issue will be addressed in
another patch.

This should be backported up to 2.6.

4 months agoMAJOR: leastconn: postpone the server's repositioning under contention
Willy Tarreau [Tue, 11 Feb 2025 16:24:19 +0000 (17:24 +0100)] 
MAJOR: leastconn: postpone the server's repositioning under contention

When leastconn is used under many threads, there can be a lot of
contention on leastconn, because the same node has to be moved around
all the time (when picking it and when releasing it). In GH issue #2861
it was noticed that 46 threads out of 64 were waiting on the same lock
in fwlc_srv_reposition().

In such a case, the accuracy of the server's key becomes quite irrelevant
because nobody cares if the same server is picked twice in a row and the
next one twice again.

While other approaches in the past considered using a floating key to
avoid moving the server each time (which was not compatible with the
round-robin rule for equal keys), here a more drastic solution is needed.
What we're doing instead is that we turn this lock into a trylock. If we
can grab it, we do the job. If we can't, then we just wake up a server's
tasklet dedicated to this. That tasklet will then try again slightly
later, knowing that during this short time frame, the server's position
in the queue is slightly inaccurate. Note that any thread touching the
same server will also reposition it and save that work for next time.
Also if multiple threads wake the tasklet up, then that's fine, their
calls will be merged and a single lock will be taken in the end.

Testing this on a 24-core EPYC 74F3 showed a significant performance
boost from 382krps to 610krps. The performance profile reported by
perf top dropped from 43% to 2.5%:

Before:
  Overhead  Shared Object             Symbol
    43.46%  haproxy-master-inlineebo  [.] fwlc_srv_reposition
    21.20%  haproxy-master-inlineebo  [.] fwlc_get_next_server
     0.91%  haproxy-master-inlineebo  [.] process_stream
     0.75%  [kernel]                  [k] ice_napi_poll
     0.51%  [kernel]                  [k] tcp_recvmsg
     0.50%  [kernel]                  [k] ice_start_xmit
     0.50%  [kernel]                  [k] tcp_ack

After:
  Overhead  Shared Object             Symbol
    30.37%  haproxy                   [.] fwlc_get_next_server
     2.51%  haproxy                   [.] fwlc_srv_reposition
     1.91%  haproxy                   [.] process_stream
     1.46%  [kernel]                  [k] ice_napi_poll
     1.36%  [kernel]                  [k] tcp_recvmsg
     1.04%  [kernel]                  [k] tcp_ack
     1.00%  [kernel]                  [k] skb_release_data
     0.96%  [kernel]                  [k] ice_start_xmit
     0.91%  haproxy                   [.] conn_backend_get
     0.82%  haproxy                   [.] connect_server
     0.82%  haproxy                   [.] run_tasks_from_lists

Tested on an Ampere Altra with 64 aarch64 cores dedicated to haproxy,
the gain is even more visible (3.6x):

  Before: 311-323k rps, 3.16-3.25ms, 6400% CPU
  Overhead  Shared Object     Symbol
    55.69%  haproxy-master    [.] fwlc_srv_reposition
    33.30%  haproxy-master    [.] fwlc_get_next_server
     0.89%  haproxy-master    [.] process_stream
     0.45%  haproxy-master    [.] h1_snd_buf
     0.34%  haproxy-master    [.] run_tasks_from_lists
     0.32%  haproxy-master    [.] connect_server
     0.31%  haproxy-master    [.] conn_backend_get
     0.31%  haproxy-master    [.] h1_headers_to_hdr_list
     0.24%  haproxy-master    [.] srv_add_to_idle_list
     0.23%  haproxy-master    [.] http_request_forward_body
     0.22%  haproxy-master    [.] __pool_alloc
     0.21%  haproxy-master    [.] http_wait_for_response
     0.21%  haproxy-master    [.] h1_send

  After: 1.21M rps, 0.842ms, 6400% CPU
  Overhead  Shared Object     Symbol
    17.44%  haproxy           [.] fwlc_get_next_server
     6.33%  haproxy           [.] process_stream
     4.40%  haproxy           [.] fwlc_srv_reposition
     3.64%  haproxy           [.] conn_backend_get
     2.75%  haproxy           [.] connect_server
     2.71%  haproxy           [.] h1_snd_buf
     2.66%  haproxy           [.] srv_add_to_idle_list
     2.33%  haproxy           [.] run_tasks_from_lists
     2.14%  haproxy           [.] h1_headers_to_hdr_list
     1.56%  haproxy           [.] stream_set_backend
     1.37%  haproxy           [.] http_request_forward_body
     1.35%  haproxy           [.] http_wait_for_response
     1.34%  haproxy           [.] h1_send

And at similar loads, the CPU usage considerably drops (3.55x), as
well as the response time (10x):

  After: 320k rps, 0.322ms, 1800% CPU
  Overhead  Shared Object     Symbol
     7.62%  haproxy           [.] process_stream
     4.64%  haproxy           [.] h1_headers_to_hdr_list
     3.09%  haproxy           [.] h1_snd_buf
     3.08%  haproxy           [.] h1_process_demux
     2.22%  haproxy           [.] __pool_alloc
     2.14%  haproxy           [.] connect_server
     1.87%  haproxy           [.] h1_send
   > 1.84%  haproxy           [.] fwlc_srv_reposition
     1.84%  haproxy           [.] run_tasks_from_lists
     1.77%  haproxy           [.] sock_conn_iocb
     1.75%  haproxy           [.] srv_add_to_idle_list
     1.66%  haproxy           [.] http_request_forward_body
     1.65%  haproxy           [.] wake_expired_tasks
     1.59%  haproxy           [.] h1_parse_msg_hdrs
     1.51%  haproxy           [.] http_wait_for_response
   > 1.50%  haproxy           [.] fwlc_get_next_server

The cost of fwlc_get_next_server() naturally increases as the server
count increases, but now has no visible effect on updates. The load
distribution remains unchanged compared to the previous approach,
the weight still being respected.

For further improvements to the fwlc algo, please consult github
issue #881 which centralizes everything related to this algorithm.

4 months agoMEDIUM: server: allocate a tasklet for asyncronous requeuing
Willy Tarreau [Tue, 11 Feb 2025 16:18:36 +0000 (17:18 +0100)] 
MEDIUM: server: allocate a tasklet for asyncronous requeuing

This creates a tasklet that only expects to be called when the LB
algorithm is under contention when trying to reposition the server
in its tree. Indeed, that's one of the operations that usually
requires to take a write lock on a highly contended area, often
for very little benefits under contention; indeed, under load, if
a server keeps its previous position for a few extra microseconds,
usually there's no harm. Thus this new tasklet can be woken up by
the LB algo to ask the server to later call lbprm.server_requeue().
It does nothing else.

4 months agoMINOR: lbprm: add a new callback ->server_requeue to the lbprm
Willy Tarreau [Tue, 11 Feb 2025 16:16:14 +0000 (17:16 +0100)] 
MINOR: lbprm: add a new callback ->server_requeue to the lbprm

This callback will be used to reposition a server to its expected
position regardless of the fact that it was taken or dropped. It
will only be used by supporting LB algos. For now, only fwlc defines
it and assigns it to fwlc_srv_reposition(). At the moment it's not
used yet.

5 months agoDEBUG: thread: reduce the struct lock_stat to store only 30 buckets
Willy Tarreau [Mon, 10 Feb 2025 10:15:44 +0000 (11:15 +0100)] 
DEBUG: thread: reduce the struct lock_stat to store only 30 buckets

Storing only 30 buckets means we only keep 256 bytes per label. This
further simplifies address calculation and reduces the memory used
without complicating the locking code. It means we won't measure wait
times larger than a second but we're not supposed to face this as it
would trigger the watchdog anyway. It may become a little bit just if
measuring using rdtsc() instead of now_mono_time() though (typically
the limit would be around 350ms for a 3 GHz CPU).

5 months agoDEBUG: thread: make lock_stat per operation instead of for all operations
Willy Tarreau [Mon, 10 Feb 2025 10:08:45 +0000 (11:08 +0100)] 
DEBUG: thread: make lock_stat per operation instead of for all operations

It's more convenient (and more readable) to have the lock stats arranged
by operation type (read, seek, write). It will also allow to later simplify
the structure format and the bucket address calculation. Now lock_stat[]
got split into lock_stats_rd[], lock_stats_sk[], lock_stats_wr[].

5 months agoDEBUG: thread: don't keep the redundant _locked counter
Willy Tarreau [Mon, 10 Feb 2025 09:56:44 +0000 (10:56 +0100)] 
DEBUG: thread: don't keep the redundant _locked counter

Now that we have our sums by bucket, the _locked counter is redundant
since it's always equal to the sum of all entries. Let's just get rid
of it and replace its consumption with a loop over all buckets, this
will reduce the overhead of taking each lock at the expense of a tiny
extra effort when dumping all locks, which we don't care about.

5 months agoDEBUG: thread: report the wait time buckets for lock classes
Willy Tarreau [Mon, 10 Feb 2025 09:41:35 +0000 (10:41 +0100)] 
DEBUG: thread: report the wait time buckets for lock classes

In addition to the total/average wait time, we now also store the
wait time in 2^N buckets. There are 32 buckets for each type (read,
seek, write), allowing to store wait times from 1-2ns to 2.1-4.3s,
which is quite sufficient, even if we'd want to switch from NS to
CPU cycles in the future. The counters are only reported for non-
zero buckets so as not to visually pollute the output.

This significantly inflates the lock_stat struct, which is now
aligned to 256 bytes and rounded up to 1kB. But that's not really
a problem, given that there's only one per lock label.

5 months agoDEBUG: thread: make lock time computation more consistent
Willy Tarreau [Mon, 10 Feb 2025 08:26:04 +0000 (09:26 +0100)] 
DEBUG: thread: make lock time computation more consistent

The lock time computation was a bit inconsistent between functions,
particularly those using a try_lock. Some of them would count the lock
as taken without counting the time, others would simply not count it.
This is essentially due to the way the time is retrieved, as it was
done inside the atomic increment.

Let's instead always use start_time to carry the elapsed time, by
presetting it to the negative time before the event and addinf the
positive time after, so that it finally contains the duration. Then
depending on the try lock's success, we add the result or not. This
was generalized to all lock functions for consistency, and because
this will be handy for future changes.

5 months agoDEBUG: thread: report the spin lock counters as seek locks
Willy Tarreau [Mon, 10 Feb 2025 07:02:42 +0000 (08:02 +0100)] 
DEBUG: thread: report the spin lock counters as seek locks

Technically speaking, spin locks use a seek lock, not a write lock,
so better count them appropriately for consistency (lock time, or
function calls count).

5 months agoBUG/MEDIUM: debug: close a possible race between thread dump and panic()
Willy Tarreau [Mon, 10 Feb 2025 16:59:40 +0000 (17:59 +0100)] 
BUG/MEDIUM: debug: close a possible race between thread dump and panic()

The rework of the thread dumping mechanism in 2.8 with commit 9a6ecbd590
("MEDIUM: debug: simplify the thread dump mechanism") opened a small
race, which is that a thread in the process of dumping other ones may
block the other one from panicing while it's looping at the end of
ha_thread_dump_fill(), or any other sequence involving the currently
dumped one.

This was emphasized in 3.1 with commit 148eb5875f ("DEBUG: wdt: better
detect apparently locked up threads and warn about them") that allowed
to emit warnings about long-stuck threads, because in this case, what
happens is that sometimes a thread starts to emit a warning (or a set
of warnings), and while the warning is being awaited for, a panic
finally happens and interrupts either the dumping thread, which never
finishes and waits for the target's pointer to become NULL which will
never happen since it was supposed to do it itself, or the currently
dumped thread which could wait for the dumping thread to become ready
while this one has not released the former.

In order to address this, first we now make sure never to dump a thread
that is already in the process of dumping another one. We're adding a
new thread flag to know this situation, that is set in ha_thread_dump_fill()
and cleared in ha_thread_dump_done(). And similarly, we don't trigger
the watchdog on a thread waiting for another one to finish its dump,
as it's likely a case of warning (and maybe even a panic) that makes
them wait for each other and we don't want such cases to be reentrant.
Finally, we check in the main polling loop that the flag never accidentally
leaked (e.g. wrong flag manipulation) as this would be difficult to spot
with bad consequences.

This should be backported at least to 2.8, and should resolve github
issue #2860. Thanks to Chris Staite for the very informative backtrace
that exhibited the problem.

5 months ago[RELEASE] Released version 3.2-dev5 v3.2-dev5
Willy Tarreau [Sat, 8 Feb 2025 04:53:40 +0000 (05:53 +0100)] 
[RELEASE] Released version 3.2-dev5

Released version 3.2-dev5 with the following main changes :
    - BUG/MINOR: ssl: put ssl_sock_load_ca under SSL_NO_GENERATE_CERTIFICATES
    - CLEANUP: ssl: rename ssl_sock_load_ca to ssl_sock_gencert_load_ca
    - CLEANUP: ssl: move ssl_sock_gencert_load_ca declaration in ssl_gencert.h
    - CLEANUP: tree-wide: define and use acl_match_cond() helper
    - MINOR: epoll: permit to mask certain specific events
    - MINOR: proxies: Add a per-thread group field to struct proxy.
    - MINOR: Add fields to the per-thread group field in struct server.
    - MINOR: proxies/servers: Calculate queueslength and use it.
    - MEDIUM: servers/proxies: Switch to using per-tgroup queues.
    - BUG/MINOR: stream: Properly handle "on-marked-up shutdown-backup-sessions"
    - MEDIUM: stream: Map task wake up reasons to dedicated stream events
    - MEDIUM: stream: No longer use TASK_F_UEVT* to shut a stream down
    - BUILD: tools: fix build on BSD by dropping the ETIME check
    - MINOR: queues: use __ha_cpu_relax() on failed CAS.
    - BUILD: queues: Use unsigned int when needed
    - BUILD: ssl: allow to build without the renegotiation API of WolfSSL
    - BUILD: ssl: more cleaner approach to WolfSSL without renegotiation
    - BUG/MEDIUM: chunk: make sure to flush the trash pool before resizing
    - MINOR: quic: remove references to burst in quic-cc-algo parsing
    - MINOR: quic: allow BBR testing without pacing
    - MINOR: quic: transform pacing settings into a global option
    - MAJOR: quic: mark pacing as stable and enable it by default
    - MINOR: quic: mark BBR as stable
    - MINOR: quic: define quic_tune
    - BUILD: quic: fix overflow in global tune
    - DEBUG: fd: add a counter of takeovers of an FD since it was last opened
    - MINOR: fd: add a generation number to file descriptors
    - DEBUG: epoll: store and compare the FD's generation count with reported event
    - MEDIUM: epoll: skip reports of stale file descriptors
    - MINOR: mux-h1: Add masks to group H1S DEMUX and MUX errors
    - BUG/MINOR: mux-h1: Only report a SE error on demux error
    - MINOR: tevt: Add the termination events log's fundations
    - MINOR: tevt/stconn: Add a termination events log in the SE descriptor
    - MINOR: tevt/mux-h1: Report termination events for the H1C and H1S
    - MINOR: tevt/mux-h2: Report termination events for the H2C
    - MINOR: tevt/stream/stconn: Report termination events for stream and sc
    - MINOR: tevt/conn: Report intercepted event for L4 rules
    - MINOR: tevt/mux-h1/mux-h2: Add termination events log when dumping mux info
    - MINOR: tevt/muxes: Add CTL and SCTL command to get the termination event logs
    - MINOR: tevt/mux-pt: Add support for termination event logs
    - MINOR: tevt/connection: Add dedicated termination events for lower locations
    - MEDIUM: tevt/muxes: Add dedicated termination events for muxc/se locations
    - MINOR: tevt/stconn: Be more accurate to report shutw events
    - MEDIUM: tevt/stconn/stream: Add dedicated termination events for stream location
    - MINOR: tevt: Don't duplicate termination event during reporting
    - MINOR: tevt/applet:  Add limited support for termination event logs for applets
    - MINOR: tevt: Add a sample to get termination events for all locations
    - MINOR: tevt: Improve function to convert a termination events log to string
    - REORG: tevt/connection: Move enums at the end of the header file
    - MINOR: tevt/dev: Add term_events tool
    - MINOR: tevt/connection: Add support for POLL_HUP/POLL_ERR events
    - MINOR: tevt/dev: Parse tuple of termination events
    - BUG/MEDIUM: htx: wrong count computation in htx_xfer_blks()
    - DOC: htx: clarify <mark> parameter for htx_xfer_blks()
    - BUILD: quic: remove GCC undefined error in qc_release_lost_pkts()
    - MEDIUM: htx: prevent <mark> to copy incomplete headers in htx_xfer_blks()
    - BUG/MEDIUM: mux-fcgi: Properly handle read0 on partial records
    - BUG/MINOR: tevt/http-ana: Remove badly placed event reports
    - DEBUG: http-ana: Remove debug counters from HTTP analyzers
    - DEBUG: mux-h1: Remove some debug counters
    - BUG/MINOR: tcp-rules: Don't forward close during tcp-response content rules eval
    - MEDIUM: stream: interrupt costly rulesets after too many evaluations
    - BUG/MINOR: http-check: Don't pretend a C-L heeader is set before adding it
    - BUILD: ssl: remove a boringssl definition defined by recent boringssl libs
    - BUG/MINOR: tevt/mux-h2: Set truncated receive/eos events at SE level on error
    - BUG/MEDIUM: flt-spoe: Set/test applet flags instead of SE flags from I/O handler
    - BUG/MEDIUM: applet: Don't pretend to have more data to handle EOI/EOS/ERROR
    - BUG/MEDIUM: flt-spoe: Properly handle end of stream from the SPOE applet
    - MINOR: flt-spoe: Report end of input immediately after applet init
    - MINOR: mux-spop: Report EOI on the SE when a ACK is received for a stream
    - MINOR: mux-spop: Set SPOP_CF_ERROR flag on connection error only
    - MINOR: tevt/mux-spop:  Report termination events for the SPOP connect/stream
    - CLEANUP: mux-spop: Remove useless comments
    - MINOR: mux-spop: Dump info about connections and streams in dedicated functions
    - MINOR: mux-spop: Implement .show_sd callback function
    - MEDIUM: mux-fcgi: Add a function to propagate termination flags from fstrm to SE
    - BUG/MEDIUM: mux-fcgi: Propagate flags to SE in fcgi_strm_wake_one_stream
    - MINOR: tevt/mux-fcgi:  Report termination events for the FCGI connect/stream
    - MINOR: mux-fcgi: Dump info about connections and streams in dedicated functions
    - MINOR: mux-spop/mux-fcgi: Add support of the debug string for logs
    - BUG/MINOR: cli: Don't set SE flags from the cli applet
    - BUG/MINOR: cli: Fix memory leak on error for _getsocks command
    - BUG/MINOR: cli: Fix a possible infinite loop in _getsocks()
    - BUG/MINOR: config/userlist: Support one 'users' option for 'group' directive
    - BUG/MINOR: auth: Fix a leak on error path when parsing user's groups
    - BUG/MINOR: flt-trace: Support only one name option
    - MINOR: filters: Improve errors formating during filters parsing
    - BUG/MINOR: stats-json: Define JSON_INT_MAX as a signed integer
    - DOC: option redispatch should mention persist options
    - BUG/MINOR: debug: make "debug dev sched" accept a negative TID
    - BUG/MINOR: debug: make sure the "debug dev sched" tasks don't block stopping
    - IMPORT: plock: export the uninlined version of the lock wait function
    - IMPORT: plock: give higher precedence to W than S
    - IMPORT: plock: lower the slope of the exponential back-off
    - IMPORT: plock: use cpu_relax() for a shorter time in EBO
    - Revert "IMPORT: plock: export the uninlined version of the lock wait function"
    - BUG/MEDIUM: ssl: chosing correct certificate using RSA-PSS with TLSv1.3

5 months agoBUG/MEDIUM: ssl: chosing correct certificate using RSA-PSS with TLSv1.3
William Lallemand [Fri, 7 Feb 2025 19:28:39 +0000 (20:28 +0100)] 
BUG/MEDIUM: ssl: chosing correct certificate using RSA-PSS with TLSv1.3

The clienthello callback was written when TLSv1.3 was not yet out, and
signatures algorithm changed since then.

With TLSv1.2, the least significant byte was used to determine the
SignatureAlgorithm, which could be rsa(1), dsa(2), ecdsa(3).
https://datatracker.ietf.org/doc/html/rfc5246#section-7.4.1.4.1

This was used to chose which type of certificate to push to the client.

But TLSv1.3 changed that, and introduced new RSA-PSS algorithms that
does not have the least sinificant byte to 1.
https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.3

This would result in chosing the wrong certificate when an RSA an ECDSA
ones are in the configuration for the same SNI or default entry.

This patch fixes the issue by parsing bothe hash and signature field to
check the RSA-PSS signature scheme.

This must fix issue #2852.

This must be backported in every stable versions. The code was moved
from ssl_sock.c to ssl_clienthello in recent versions.

5 months agoRevert "IMPORT: plock: export the uninlined version of the lock wait function"
Willy Tarreau [Fri, 7 Feb 2025 18:51:15 +0000 (19:51 +0100)] 
Revert "IMPORT: plock: export the uninlined version of the lock wait function"

This reverts commit 5496d06b2b1ea276ffb6aec78ffca177b88d89cd.

It breaks the build on Windows which apparently doesn't support the weak
attribute well on functions. It's not big deal anyway, playing with build
options while debugging still works though it's less easy to use.

5 months agoIMPORT: plock: use cpu_relax() for a shorter time in EBO
Willy Tarreau [Fri, 7 Feb 2025 16:33:49 +0000 (17:33 +0100)] 
IMPORT: plock: use cpu_relax() for a shorter time in EBO

Tests have shown that on modern CPUs it's interesting to wait a bit less
in cpu_relax(). Till now we were looping down to 60 iterations and then
switching to just barriers. Increasing the threshold to 90 iterations
left before getting out of the loop improved the average and max time
to grab a write lock by a few percent (e.g. 10% at 1us, 20% at 256ns
or lower). Higher values tend to progressively lose that gain so let's
stick to this one. This was measured on an EPYC 74F3 like previous
measurements that initially led to this value, and the value might
possibly depend on the mask applied to the loop counter.

This is plock commit 74ca0a7307fa6aec3139f27d3b7e534e1bdb748e.

5 months agoIMPORT: plock: lower the slope of the exponential back-off
Willy Tarreau [Fri, 7 Feb 2025 16:20:48 +0000 (17:20 +0100)] 
IMPORT: plock: lower the slope of the exponential back-off

Along many tests involving both haproxy's scheduler and forwarded
traffic, various exponents and algorithms were attempted for the EBO
and their effects were measured. It was found that a growth in 1.25^N
limited to 128k cycles consistently gives a better latency than 1.5^N
limited to 256k cycles, without degrading general performance. The
measures of the time to grab a write lock on a 48-thread EPYC show
that the number of occurrences of low times was roughly multiplied by
2-3 while the number of occurrences of times above 64us was reduced
by similar factors, to even reach 300 at 64us and limiting the maximum
time by a factor of 4.

The other variants that were experimented with are:

  m = ((m + (m >> 1)) + 2) & 0x3ffff;            // original
  m = ((m + (m >> 1) + (m >> 3)) + 2) & 0x3ffff;
  m = ((m + (m >> 1) + (m >> 4)) + 2) & 0x3ffff;
  m = ((m + (m >> 1) + (m >> 4)) + 2) & 0x1ffff;
  m = ((m + (m >> 1) + (m >> 4)) + 1) & 0x1ffff;
  m = ((m + (m >> 2) + (m >> 4)) + 1) & 0x1ffff; // lowest CPU on pl_wr test + good perf
  m = ((m + (m >> 2)) + 1) & 0x1ffff;            // even lower cpu usage, lowest max
  m = ((m + (m >> 1) + (m >> 2)) + 1) & 0x1ffff; // correct but slightly higher maxes
  m = ((m + (m >> 1) + (m >> 3)) + 1) & 0x1ffff; // less good than m+m>>2
  m = ((m + (m >> 2) + (m >> 3)) + 1) & 0x1ffff; // better but not as good as m+m>>2
  m = ((m + (m >> 3) + (m >> 4)) + 1) & 0x1ffff; // less good, lower rates on small coounts.
  m = ((m + (m >> 2) + (m >> 3) + (m >> 4)) + 1) & 0x1ffff; // less good as well
  m = ((m & 0x7fff) + (m >> 1) + (m >> 4)) + 2;
  m = ((m & 0xffff) + (m >> 1) + (m >> 4)) + 2;

This is plock commit dddd9ee01c522da33c353e2e4d4fd743d8336ec3.

5 months agoIMPORT: plock: give higher precedence to W than S
Willy Tarreau [Fri, 7 Feb 2025 15:57:28 +0000 (16:57 +0100)] 
IMPORT: plock: give higher precedence to W than S

It was noticed in haproxy that in certain extreme cases, a write lock
subject to EBO may fail for a very long time in front of a large set
of readers constantly trying to upgrade to the S state. The reason is
that among many readers, one will succeed in its upgrade, and this
situation can last for a very long time with many readers upgrading
in turn, while the writer waits longer and longer before trying again.

Here we're taking a reasonable approach which is that the write lock
should have a higher precedence in its attempt to grab the lock. What
is done is that instead of fully rolling back in case of conflict with
a pure S lock, the writer will only release its read part in order to
let the S upgrade to W if needed, and finish its operations. This
guarantees no other seek/read/write can enter. Once the conflict is
resolved, the writer grabs the read part again and waits for readers
to be gone (in practice it could even return without waiting since we
know that any possible wanderers would leave or even not be there at
all, but it avoids a complicated loop code that wouldn't improve the
practical situation but inflate the code).

Thanks to this change, the maximum write lock latency on a 48 threads
AMD with aheavily loaded scheduler went down from 256 to 64 ms, and the
number of occurrences of 32ms or more was divided by 300, while all
occurrences of 1ms or less were multiplied by up to 3 (3 for the 4-16ns
cases).

This is plock commit b6a28366d156812f59c91346edc2eab6374a5ebd.

5 months agoIMPORT: plock: export the uninlined version of the lock wait function
Willy Tarreau [Fri, 7 Feb 2025 15:45:21 +0000 (16:45 +0100)] 
IMPORT: plock: export the uninlined version of the lock wait function

The inlining of the lock waiting function was made more easily
configurable with commit 7505c2e ("plock: always expose the inline
version of the lock wait function"). However, the standard one remained
static, but in order to resolve the symbols in "perf top", it's much
better to export it, so let's move "static" with "inline" and leave it
exported when PLOCK_INLINE_EBO is not set.

This is plock commit 3bea7812ec705b9339bbb0ed482a2cd8aa6c185c.

5 months agoBUG/MINOR: debug: make sure the "debug dev sched" tasks don't block stopping
Willy Tarreau [Fri, 7 Feb 2025 17:01:32 +0000 (18:01 +0100)] 
BUG/MINOR: debug: make sure the "debug dev sched" tasks don't block stopping

When "debug dev sched" is used to pop up background tasks, these tasks
are never stopped, so we must be careful to stop them when the stopping
flag is set, otherwise they can prevent the process from stopping when
sufficiently numerous (tests went as far as 100 million tasks, leading
the run queue never being completely purged in one poll round).

No backport is needed since this is only used when debugging and tuning
the scheduler.

5 months agoBUG/MINOR: debug: make "debug dev sched" accept a negative TID
Willy Tarreau [Fri, 7 Feb 2025 16:59:11 +0000 (17:59 +0100)] 
BUG/MINOR: debug: make "debug dev sched" accept a negative TID

The TID passed to "debug dev sched" is used to pin the task to a given
thread. A negative value normally means the task is unpinned and goes
to the shared wait queue and run queue. However due to the type of the
variable, negative values were mapped as highly positive values and were
set to the current thread. Let's add the proper cast to fix this.

No backport is needed since this is only used to experiment with the
scheduler and measure its performance.

5 months agoDOC: option redispatch should mention persist options
Lukas Tribus [Wed, 5 Feb 2025 07:42:15 +0000 (07:42 +0000)] 
DOC: option redispatch should mention persist options

"option redispatch" remains vague in which cases a session would persist;
let's mention "option persist" and "force-persist" as an example so folks
don't draw the conclusion that this may be default.

Should be backported to stable branches.

5 months agoBUG/MINOR: stats-json: Define JSON_INT_MAX as a signed integer
Christopher Faulet [Thu, 6 Feb 2025 16:13:50 +0000 (17:13 +0100)] 
BUG/MINOR: stats-json: Define JSON_INT_MAX as a signed integer

A JSON integer is defined in the range [-(2**53)+1, (2**53)-1]. Macro are used
to define the minimum and the maximum value, The minimum one is defined using
the maximum one. So JSON_INT_MAX must be defined as a signed integer value to
avoid wrong cast of JSON_INT_MIN.

It was reported by Coverity in #2841: CID 1587769.

This patch could be backported to all stable versions.

5 months agoMINOR: filters: Improve errors formating during filters parsing
Christopher Faulet [Thu, 6 Feb 2025 16:03:35 +0000 (17:03 +0100)] 
MINOR: filters: Improve errors formating during filters parsing

The error message reported by a filter during parsing are displayed between
quotes. It is not really user friendly. So let's remove the quotes here.

5 months agoBUG/MINOR: flt-trace: Support only one name option
Christopher Faulet [Thu, 6 Feb 2025 16:01:08 +0000 (17:01 +0100)] 
BUG/MINOR: flt-trace: Support only one name option

When a trace filter is defined, only one 'name' option is expected. But it
was not tested. Thus it was possible to set several names leading to a
memory leak.

It is now tested, and it is not allowed to redefine the trace filter name.

It was reported by Coverity in #2841: CID 1587768.

This patch could be backported to all stable versions.

5 months agoBUG/MINOR: auth: Fix a leak on error path when parsing user's groups
Christopher Faulet [Thu, 6 Feb 2025 15:52:17 +0000 (16:52 +0100)] 
BUG/MINOR: auth: Fix a leak on error path when parsing user's groups

In a userlist section, when a user is parsed, if a specified group is not
found, an error is reported. In this case we must take care to release the
alredy built groups list.

It was reported by Coverity in #2841: CID 1587770.

This patch could be backported to all stable versions.

5 months agoBUG/MINOR: config/userlist: Support one 'users' option for 'group' directive
Christopher Faulet [Thu, 6 Feb 2025 15:21:20 +0000 (16:21 +0100)] 
BUG/MINOR: config/userlist: Support one 'users' option for 'group' directive

When a group is defined in a userlist section, only one 'users' option is
expected. But it was not tested. Thus it was possible to set several options
leading to a memory leak.

It is now tested, and it is not allowed to redefine the users option.

It was reported by Coverity in #2841: CID 1587771.

This patch could be backported to all stable versions.

5 months agoBUG/MINOR: cli: Fix a possible infinite loop in _getsocks()
Christopher Faulet [Thu, 6 Feb 2025 14:37:52 +0000 (15:37 +0100)] 
BUG/MINOR: cli: Fix a possible infinite loop in _getsocks()

In _getsocks() functuoin, when we failed to set the unix socket in
non-blocking mode, a goto to "out" label led to loop infinitly. To fix the
issue, we must only let the function exit.

This patch should be backported to all stable versions.

5 months agoBUG/MINOR: cli: Fix memory leak on error for _getsocks command
Christopher Faulet [Thu, 6 Feb 2025 14:30:30 +0000 (15:30 +0100)] 
BUG/MINOR: cli: Fix memory leak on error for _getsocks command

Some errors in parse function of _getsocks commands were not properly handled
and immediately returned, leading to a memory leak on cmsgbuf and tmpbuf
buffers.

To fix the issue, instead of immediately return with -1, we jump to "out"
label. Returning 1 intead of -1 in that case is valid.

This was reported by Coverity in #2841: CIDs 1587773 and 1587772.

This patch should be backported as far as 2.4.

5 months agoBUG/MINOR: cli: Don't set SE flags from the cli applet
Christopher Faulet [Thu, 6 Feb 2025 14:15:27 +0000 (15:15 +0100)] 
BUG/MINOR: cli: Don't set SE flags from the cli applet

Since the CLI was updated to use the new applet API, it should no longer set
directly the SE flags. Instead, the corresponding applet flags must be set,
using the applet API (appet_set_*). It is true for the CLI I/O handler but also
for the commands parse function and I/O callback function.

This patch should be backported as far as 3.0.

5 months agoMINOR: mux-spop/mux-fcgi: Add support of the debug string for logs
Christopher Faulet [Wed, 5 Feb 2025 15:04:26 +0000 (16:04 +0100)] 
MINOR: mux-spop/mux-fcgi: Add support of the debug string for logs

Now it is possible to have debug info about FCGI and SPOP multiplexers. To do
so, the support for the MUX_SCTL_DBG_STR command was implemented for these
muxes.

The have this log message, the log-format must be set to:

  log-format "$HAPROXY_HTTP_LOG_FMT bs=<%[bs.debug_str]>"

5 months agoMINOR: mux-fcgi: Dump info about connections and streams in dedicated functions
Christopher Faulet [Wed, 5 Feb 2025 14:56:36 +0000 (15:56 +0100)] 
MINOR: mux-fcgi: Dump info about connections and streams in dedicated functions

fcgi_show_fd() function was splitted to dump the info about the FCGI
connections and the FCGI streams in dedicated functions, duplicating this
way what is performed in other muxes.

In addition, the FCGI multiplexer now implements the .show_sd callback
function called by "show sess" CLI command.

5 months agoMINOR: tevt/mux-fcgi: Report termination events for the FCGI connect/stream
Christopher Faulet [Wed, 5 Feb 2025 14:45:39 +0000 (15:45 +0100)] 
MINOR: tevt/mux-fcgi:  Report termination events for the FCGI connect/stream

Termination events are now reported for the FCGI connections and the FCGI
streams. In addition, all available termination events logs are reported in
the "show-fd" callback function. The .ctl and .sctl callback functions were
also update to support, respectively, MUX_CTL_TEVTS and MUX_SCTL_TEVTS
commands.

5 months agoBUG/MEDIUM: mux-fcgi: Propagate flags to SE in fcgi_strm_wake_one_stream
Christopher Faulet [Wed, 5 Feb 2025 14:06:45 +0000 (15:06 +0100)] 
BUG/MEDIUM: mux-fcgi: Propagate flags to SE in fcgi_strm_wake_one_stream

The commit is flagged as a bug because the same fix on the H2 multiplexer was
reported as a bug. But no issue was reported.

When a stream is explicitly woken up by the FCGI conneciton, if an error
condition is detected, the corresponding error flag is set on the SE. So
SE_FL_ERROR or SE_FL_ERR_PENDING, depending if the end of stream was
reported or not.

However, there is no attempt to propagate other termination flags. We must
be sure to properly set SE_FL_EOI and SE_FL_EOS when appropriate to be able
to switch a pending error to a fatal error.

Because of this bug, the SE could remain with a pending error and no end of
stream, preventing the applicative stream to trully abort it. It means on
some abort scenario, it seems to be possible to block a stream infinitely.

This patche depends on:

  * MEDIUM: mux-fcgi: Add a function to propagate termination flags from fstrm to SE
  * BUG/MEDIUM: mux-fcgi: Properly handle read0 on partial records

This patch could be backported at least as far as 2.8 after a period of
observation. However no bug was reportedn so there is no rush.

5 months agoMEDIUM: mux-fcgi: Add a function to propagate termination flags from fstrm to SE
Christopher Faulet [Wed, 5 Feb 2025 13:28:47 +0000 (14:28 +0100)] 
MEDIUM: mux-fcgi: Add a function to propagate termination flags from fstrm to SE

The function fcgi_strm_propagate_term_flags() was added to check the FSTRM
state and evaluate when EOI/EOS/ERR_PENDING/ERROR flags must be set on the
SE. It is not the only place where those flags are set. But it centralizes
the synchro between the FCGI stream and the SC.

For now, this function is only used at the end of fcgi_rcv_buf(). But it
will be used to fix a potential bug.

5 months agoMINOR: mux-spop: Implement .show_sd callback function
Christopher Faulet [Tue, 4 Feb 2025 10:54:05 +0000 (11:54 +0100)] 
MINOR: mux-spop: Implement .show_sd callback function

The SPOP multiplexer now implements the .show_sd callback function called by
"show sess" CLI command.

5 months agoMINOR: mux-spop: Dump info about connections and streams in dedicated functions
Christopher Faulet [Tue, 4 Feb 2025 10:50:17 +0000 (11:50 +0100)] 
MINOR: mux-spop: Dump info about connections and streams in dedicated functions

spop_show_fd() function was splitted to dump the info about the SPOP
connections and the SPOP streams in dedicated functions, duplicating this
way what is performed in other muxes.

5 months agoCLEANUP: mux-spop: Remove useless comments
Christopher Faulet [Tue, 4 Feb 2025 10:21:23 +0000 (11:21 +0100)] 
CLEANUP: mux-spop: Remove useless comments

Just a small cleanup to remove some comments added during the development of
the mux.

5 months agoMINOR: tevt/mux-spop: Report termination events for the SPOP connect/stream
Christopher Faulet [Tue, 4 Feb 2025 10:03:31 +0000 (11:03 +0100)] 
MINOR: tevt/mux-spop:  Report termination events for the SPOP connect/stream

Termination events are now reported for the SPOP connections and the SPOP
streams. In addition, all available termination events logs are reported in
the "show-fd" callback function. The .ctl and .sctl callback functions were
also update to support, respectively, MUX_CTL_TEVTS and MUX_SCTL_TEVTS
commands.

5 months agoMINOR: mux-spop: Set SPOP_CF_ERROR flag on connection error only
Christopher Faulet [Tue, 4 Feb 2025 09:58:35 +0000 (10:58 +0100)] 
MINOR: mux-spop: Set SPOP_CF_ERROR flag on connection error only

The SPOP_CF_ERROR flag is now set on connection error only. It was also set
on some demux failures. But it is not mandatory because the connection is
closed anyway. And it is handy to have a flag dedicated to tcp connection
error. It was the original purpose of this flag.

This patch could be backported to 3.1 to ease future backports.

5 months agoMINOR: mux-spop: Report EOI on the SE when a ACK is received for a stream
Christopher Faulet [Tue, 4 Feb 2025 09:53:20 +0000 (10:53 +0100)] 
MINOR: mux-spop: Report EOI on the SE when a ACK is received for a stream

The spop stream now reports the end of input when the ACK is transferred to
the SPOE applet. To do so, the flag SPOP_SF_ACK_RCVD was added. It is set on
the SPOP stream when its ACK is received by the SPOP connection.

In addition when SPOP stream flags are propagated to the SE, the error is
now reported if end of input was not reached instead of testing the
connection error code. It is more accurate.

This patch should be backported to 3.1.

5 months agoMINOR: flt-spoe: Report end of input immediately after applet init
Christopher Faulet [Tue, 4 Feb 2025 09:46:28 +0000 (10:46 +0100)] 
MINOR: flt-spoe: Report end of input immediately after applet init

The SPOE applet forwards the message that must be sent to agent during its
init stage. So just after it is created. When it is performed, the end of
input must be reported because no more data will be forwarded. However, it
was performed after receiving the ACK response. It is harmless, but there is
no reason to delay the EOI. It is now fixed.

This patch must be backported to 3.1.

5 months agoBUG/MEDIUM: flt-spoe: Properly handle end of stream from the SPOE applet
Christopher Faulet [Tue, 4 Feb 2025 17:05:33 +0000 (18:05 +0100)] 
BUG/MEDIUM: flt-spoe: Properly handle end of stream from the SPOE applet

The previous fix ("BUG/MEDIUM: applet: Don't pretend to have more data to
handle EOI/EOS/ERROR") revealed an issue with the way the SPOE applet was
reporting the end of stream, leading to never shut the applet down.

In fact, there is two bug in one. The first one is about the applet
shutdown. Since the fix above, the applet is no longer closed. Before, it
was closed because it was reported in error. But now, it is just delayed
because the applet and the SPOP stream are declared to support half close
connections. So the applet is only closed when the SPOP connection is
closed. To fix this bug, both side are now stating that half close
connections are not supported.

The second bug is about the way the end of stream is reported. It is
reported when the ACK response is received. But it is too early, because the
parent stream must process the response first. So now, we take care to have
processed the ACK from the parent applet before reporting an end of stream.

This patch must be backported with the commit above to 3.1.

5 months agoBUG/MEDIUM: applet: Don't pretend to have more data to handle EOI/EOS/ERROR
Christopher Faulet [Tue, 4 Feb 2025 08:20:36 +0000 (09:20 +0100)] 
BUG/MEDIUM: applet: Don't pretend to have more data to handle EOI/EOS/ERROR

The way appctx EOI/EOS/ERROR flags were reported for applets using the new
API were to state the applet had more data to deliver. But it was not
correct and for APPCTX_FL_EOS, this led to report an error on the SE because
it is not expected. More data to deliver and an end of stream is an
impossible situation.

This was added as a fix by commit b8ca114031 ("BUG/MEDIUM: applet: State
appctx have more data if its EOI/EOS/ERROR flag is set"), mainly to make the
SPOE applet work.

When an applet set one of these flags, it really means it has no more data
to deliver. So we must not try to trigger a new receive to handle these
flags. Instead we must handle them directly in task_process_applet()
function and only if the corresponding SE flags were not already set.

This patch must be backported to 3.1.

5 months agoBUG/MEDIUM: flt-spoe: Set/test applet flags instead of SE flags from I/O handler
Christopher Faulet [Tue, 4 Feb 2025 09:42:19 +0000 (10:42 +0100)] 
BUG/MEDIUM: flt-spoe: Set/test applet flags instead of SE flags from I/O handler

The SPOE applet is using the new applet API. Thus end of input, end of
stream and errors must be reported using the applet flags, not the SE
flags. This was not the case. So let's fix it.

It seems this bug is harmless for now.

This patch must be backported to 3.1.

5 months agoBUG/MINOR: tevt/mux-h2: Set truncated receive/eos events at SE level on error
Christopher Faulet [Tue, 4 Feb 2025 07:21:06 +0000 (08:21 +0100)] 
BUG/MINOR: tevt/mux-h2: Set truncated receive/eos events at SE level on error

When receive or EOS termination events are reported at the SE level, a
truncation was erroneously reported when no error was detected. Of course, it
must be the opposite.

No backport needed.

5 months agoBUILD: ssl: remove a boringssl definition defined by recent boringssl libs
Frederic Lecaille [Thu, 6 Feb 2025 09:48:25 +0000 (10:48 +0100)] 
BUILD: ssl: remove a boringssl definition defined by recent boringssl libs

This is the case for AWS-LC which derives from boringssl, where
X509_OBJECT_get0_X509_CRL() is already defined. There is definitively
no more need to define this function to build haproxy against TLS libs derived
from boringssl.

5 months agoBUG/MINOR: http-check: Don't pretend a C-L heeader is set before adding it
Christopher Faulet [Mon, 3 Feb 2025 17:36:17 +0000 (18:36 +0100)] 
BUG/MINOR: http-check: Don't pretend a C-L heeader is set before adding it

When a GET/HEAD/OPTIONS/DELETE healthcheck request was formatted, we claimed
there was a "content-length" header set even when there was no payload,
leading to actually send a "content-length: 0" header to the server. It was
unexpected and could be rejected by servers.

When a healthcheck request is sent we must take care to state there is a
"content-length" header when it is explicitly added.

This patch should fix the issue #2851. It must be backported as far as 2.9.

5 months agoMEDIUM: stream: interrupt costly rulesets after too many evaluations
Aurelien DARRAGON [Thu, 30 Jan 2025 12:26:42 +0000 (13:26 +0100)] 
MEDIUM: stream: interrupt costly rulesets after too many evaluations

It is not rare to see configurations with a large number of "tcp-request
content" or "http-request" rules for instance. A large number of rules
combined with cpu-demanding actions (e.g.: actions that work on content)
may create thread contention as all the rules from a given ruleset are
evaluated under the same polling loop if the evaluation is not interrupted

Thus, in this patch we add extra logic around "tcp-request content",
"tcp-response content", "http-request" and "http-response" rulesets, so
that when a certain number of rules are evaluated under the single polling
loop, we force the evaluating function to yield. As such, the rule which
was about to be evaluated is saved, and the function starts evaluating
rules from the save pointer when it returns (in the next polling loop).

We use task_wakeup(task, TASK_WOKEN_MSG) to explicitly wake the task so
that no time is wasted and the processing is resumed ASAP. TASK_WOKEN_MSG
is mandatory here because process_stream() expects TASK_WOKEN_MSG for
explicit analyzers re-evaluation.

rules_bcount stream's attribute was added to count how manu rules were
evaluated since last interruption (yield). Also, SF_RULE_FYIELD flag
was added to know that the s->current_rule was assigned due to forced
yield and not regular yield.

By default haproxy will enforce a yield every 50 rules, this behavior
can be configured using the "tune.max-rules-at-once" global keyword.

There is a limitation though: for now, if the ACT_OPT_FINAL flag is set
on act_opts, we consider it is not safe to yield (as it is already the
case for automatic yield). In this case instead of yielding an taking
the risk of not being called back, we skip the yield and hope it will
not create contention. This is something we should ideally try to
improve in order to yield in all conditions.

5 months agoBUG/MINOR: tcp-rules: Don't forward close during tcp-response content rules eval
Christopher Faulet [Mon, 3 Feb 2025 14:31:57 +0000 (15:31 +0100)] 
BUG/MINOR: tcp-rules: Don't forward close during tcp-response content rules eval

When the tcp-response content ruleset evaluation is delayed because of an
ACL condition, the close forwarding on the client side is not explicitly
blocked. So it is possible to close the client side before the end of the
response evaluation.

To fix the issue, this is now done in all cases where some data are
missing. Concretely, channel_dont_close() is called in "missing_data" goto
label.

Note it is only a theorical bug (or pending bug). It is not possible to
trigger it for now because an ACL cannot wait for more data when a close was
received. But the code remains a bit weak. It is safer this way. It is
especially mandatory for the "force yield" option that should be added soon.

This patch could be backported to all stable versions.

5 months agoDEBUG: mux-h1: Remove some debug counters
Christopher Faulet [Mon, 3 Feb 2025 07:48:30 +0000 (08:48 +0100)] 
DEBUG: mux-h1: Remove some debug counters

Several debug counters were added to debug a strange issue about early
aborts. Most of them are now useless, especially because it is now possible
to rely on the termination events logs. So, it is better to remove them.

Note that these counters are still there in 3.1.

5 months agoDEBUG: http-ana: Remove debug counters from HTTP analyzers
Christopher Faulet [Mon, 3 Feb 2025 07:28:43 +0000 (08:28 +0100)] 
DEBUG: http-ana: Remove debug counters from HTTP analyzers

Several debug counters were added in HTTP analyzers to help debugging a
strange issue about early aborts. But these counters are a bit overkill
now. Especially because it is now possible to rely on the termination event
log. So just remove them.

Note that these counters are still there in 3.1.

5 months agoBUG/MINOR: tevt/http-ana: Remove badly placed event reports
Christopher Faulet [Mon, 3 Feb 2025 07:20:40 +0000 (08:20 +0100)] 
BUG/MINOR: tevt/http-ana: Remove badly placed event reports

When specific events for the stream location were added, some reports about
message interception were not removed. These reports are now removed.

No need to backport.

5 months agoBUG/MEDIUM: mux-fcgi: Properly handle read0 on partial records
Christopher Faulet [Mon, 27 Jan 2025 14:18:14 +0000 (15:18 +0100)] 
BUG/MEDIUM: mux-fcgi: Properly handle read0 on partial records

A Read0 event could be ignored by the FCGI multiplexer if it is blocked on a
partial record. Instead of handling the event, it remained blocked, waiting
for the end of the record.

To fix the issue, the same solution than the H2 multiplexer is used. Two
flags are introduced. The first one, FCGI_CF_END_REACHED, is used to
acknowledge a read0. This flag is set when a read0 was received AND the FCGI
multiplexer must handle it. The second one, FCGI_CF_DEM_SHORT_READ, is set
when the demux is interrupted on a partial record. A short read and a read0
lead to set the FCGI_CF_END_REACHED flag.

With these changes, the FCGI mux should be able to properly handle read0 on
partial records.

This patch should be backported to all stable versions after a period of
observation.

5 months agoMEDIUM: htx: prevent <mark> to copy incomplete headers in htx_xfer_blks()
William Lallemand [Fri, 31 Jan 2025 14:31:00 +0000 (15:31 +0100)] 
MEDIUM: htx: prevent <mark> to copy incomplete headers in htx_xfer_blks()

Prevent a partial copy of trailers or headers when using the <mark>
parameter.

When using htx_xfer_blks(), transfering partial headers or trailers are
prevented when restricted by the <count> parameter. However using the
<mark> parameter will still allow to do it.

This patch changes the behavior by checking the <mark> type only after
checking the headers/trailers type, so we can still rollback on partial
transfer.

No impact on the current code, which does not try to do that yet.

5 months agoBUILD: quic: remove GCC undefined error in qc_release_lost_pkts()
Amaury Denoyelle [Thu, 30 Jan 2025 15:24:10 +0000 (16:24 +0100)] 
BUILD: quic: remove GCC undefined error in qc_release_lost_pkts()

Every once in a while, GCC reports issues with qc_release_lost_pkts()
function. It seems that its static analysis is foiled by the code
structuring. The latest warning reports the following issue :

  CC      src/quic_loss.o
src/quic_loss.c: In function ‘qc_release_lost_pkts’:
src/quic_loss.c:313:58: error: potential null pointer dereference [-Werror=null-dereference]
  313 |                         unsigned int period = newest_lost->time_sent_ms - oldest_lost->time_sent_ms;
      |                                               ~~~~~~~~~~~^~~~~~~~~~~~~~

To fix definitely this, change slightly the code. <oldest_lost> and
<newest_lost> are now initialized on the first list entry outside of the
loop. This is enough to guarantee to GCC that they cannot be NULL for
the remainder of the function.