Willy Tarreau [Thu, 14 May 2026 20:37:14 +0000 (20:37 +0000)]
BUG/MINOR: resolvers: fix missing task_idle destruction in resolvers_destroy()
When destroying a stream-based DNS nameserver, task_req and task_rsp
were destroyed but task_idle was missed, causing a task object leak.
This doesn't necessarily have to be backported since it's only upon
exit that it is visible.
Willy Tarreau [Thu, 14 May 2026 21:40:59 +0000 (21:40 +0000)]
BUG/MINOR: resolvers: fix leaked fields on cfg_parse_resolvers() error paths
cfg_parse_resolvers() has many error paths on allocation failure when
parsing "nameserver". These paths handle their own cleanup instead of
centralizing it. The result is that some errors paths leak some fields.
The most complex ones are the strdup() failures which require to check
for stream or dgram to figure what to free. These can be detected via
ASAN on a dummy strdup() allocation failure:
Indirect leak of 131080 byte(s) in 1 object(s) allocated from:
#0 0x7f0b7ed1f0ab in malloc (/usr/lib64/libasan.so.8+0x11f0ab)
#1 0x000000c73e19 in dns_ring_new src/dns_ring.c:59
#2 0x000000af1848 in dns_dgram_init src/dns.c:480
#3 0x000000922005 in cfg_parse_resolvers src/resolvers.c:3792
#4 0x00000089d33e in parse_cfg src/cfgparse.c:2202
#5 0x0000009e0a39 in read_cfg src/haproxy.c:1142
#6 0x000000447e8c in main src/haproxy.c:3474
#7 0x7f0b7e02ad13 in __libc_start_call_main (/lib64/libc.so.6+0x2ad13)
#8 0x7ffd35f1531c ([stack]+0x2031c)
Indirect leak of 304 byte(s) in 1 object(s) allocated from:
#0 0x7f0b7ed1ea23 in calloc (/usr/lib64/libasan.so.8+0x11ea23)
#1 0x000000af1681 in dns_dgram_init src/dns.c:468
#2 0x000000922005 in cfg_parse_resolvers src/resolvers.c:3792
#3 0x00000089d33e in parse_cfg src/cfgparse.c:2202
#4 0x0000009e0a39 in read_cfg src/haproxy.c:1142
#5 0x000000447e8c in main src/haproxy.c:3474
#6 0x7f0b7e02ad13 in __libc_start_call_main (/lib64/libc.so.6+0x2ad13)
#7 0x7ffd35f1531c ([stack]+0x2031c)
Indirect leak of 104 byte(s) in 1 object(s) allocated from:
#0 0x7f0b7ed1ea23 in calloc (/usr/lib64/libasan.so.8+0x11ea23)
#1 0x000000921f83 in cfg_parse_resolvers src/resolvers.c:3772
#2 0x00000089d33e in parse_cfg src/cfgparse.c:2202
#3 0x0000009e0a39 in read_cfg src/haproxy.c:1142
#4 0x000000447e8c in main src/haproxy.c:3474
#5 0x7f0b7e02ad13 in __libc_start_call_main (/lib64/libc.so.6+0x2ad13)
#6 0x7ffd35f1531c ([stack]+0x2031c)
Indirect leak of 64 byte(s) in 1 object(s) allocated from:
#0 0x7f0b7ed1f0ab in malloc (/usr/lib64/libasan.so.8+0x11f0ab)
#1 0x000000c73e09 in dns_ring_new src/dns_ring.c:55
#2 0x000000af1848 in dns_dgram_init src/dns.c:480
#3 0x000000922005 in cfg_parse_resolvers src/resolvers.c:3792
#4 0x00000089d33e in parse_cfg src/cfgparse.c:2202
#5 0x0000009e0a39 in read_cfg src/haproxy.c:1142
#6 0x000000447e8c in main src/haproxy.c:3474
#7 0x7f0b7e02ad13 in __libc_start_call_main (/lib64/libc.so.6+0x2ad13)
#8 0x7ffd35f1531c ([stack]+0x2031c)
Indirect leak of 15 byte(s) in 1 object(s) allocated from:
#0 0x7f0b7ed18e20 in strdup (/usr/lib64/libasan.so.8+0x118e20)
#1 0x00000092203b in cfg_parse_resolvers src/resolvers.c:3798
#2 0x00000089d33e in parse_cfg src/cfgparse.c:2202
#3 0x0000009e0a39 in read_cfg src/haproxy.c:1142
#4 0x000000447e8c in main src/haproxy.c:3474
#5 0x7f0b7e02ad13 in __libc_start_call_main (/lib64/libc.so.6+0x2ad13)
#6 0x7ffd35f1531c ([stack]+0x2031c)
This should be completely reworked so that the cleanup is performed in
a central place, as the risk to get it wrong remains high.
This patch does the minimal changes to clean this up. It does not need
to be backported since it only triggers on boot OOM.
Willy Tarreau [Thu, 14 May 2026 21:37:18 +0000 (21:37 +0000)]
BUG/MINOR: resolvers: fix leaked dgram and dns_ring struct in parse_resolve_conf()
Some strdup() failures in parse_resolve_conf() do not release everything
due to the way the function is built, resulting in leaks on error that are
caught by ASAN:
Direct leak of 304 byte(s) in 1 object(s) allocated from:
#0 0x7fe74231ea23 in calloc (/usr/lib64/libasan.so.8+0x11ea23)
#1 0x000000af1681 in dns_dgram_init src/dns.c:468
#2 0x00000091cbbf in parse_resolve_conf src/resolvers.c:3559
#3 0x00000092179e in cfg_parse_resolvers src/resolvers.c:3815
#4 0x00000089d33e in parse_cfg src/cfgparse.c:2202
#5 0x0000009e0a39 in read_cfg src/haproxy.c:1142
#6 0x000000447e8c in main src/haproxy.c:3474
#7 0x7fe74162ad13 in __libc_start_call_main (/lib64/libc.so.6+0x2ad13)
#8 0x7ffc0a43e31f ([stack]+0x2031f)
Indirect leak of 131080 byte(s) in 1 object(s) allocated from:
#0 0x7fe74231f0ab in malloc (/usr/lib64/libasan.so.8+0x11f0ab)
#1 0x000000c73e19 in dns_ring_new src/dns_ring.c:59
#2 0x000000af1848 in dns_dgram_init src/dns.c:480
#3 0x00000091cbbf in parse_resolve_conf src/resolvers.c:3559
#4 0x00000092179e in cfg_parse_resolvers src/resolvers.c:3815
#5 0x00000089d33e in parse_cfg src/cfgparse.c:2202
#6 0x0000009e0a39 in read_cfg src/haproxy.c:1142
#7 0x000000447e8c in main src/haproxy.c:3474
#8 0x7fe74162ad13 in __libc_start_call_main (/lib64/libc.so.6+0x2ad13)
#9 0x7ffc0a43e31f ([stack]+0x2031f)
Indirect leak of 64 byte(s) in 1 object(s) allocated from:
#0 0x7fe74231f0ab in malloc (/usr/lib64/libasan.so.8+0x11f0ab)
#1 0x000000c73e09 in dns_ring_new src/dns_ring.c:55
#2 0x000000af1848 in dns_dgram_init src/dns.c:480
#3 0x00000091cbbf in parse_resolve_conf src/resolvers.c:3559
#4 0x00000092179e in cfg_parse_resolvers src/resolvers.c:3815
#5 0x00000089d33e in parse_cfg src/cfgparse.c:2202
#6 0x0000009e0a39 in read_cfg src/haproxy.c:1142
#7 0x000000447e8c in main src/haproxy.c:3474
#8 0x7fe74162ad13 in __libc_start_call_main (/lib64/libc.so.6+0x2ad13)
#9 0x7ffc0a43e31f ([stack]+0x2031f)
SUMMARY: AddressSanitizer: 131448 byte(s) leaked in 3 allocation(s).
Let's free the dgram and the dns ring. This can be backported though it's
not important as it only happens on OOM condition during boot.
Willy Tarreau [Thu, 14 May 2026 22:43:17 +0000 (22:43 +0000)]
BUG/MINOR: resolvers: report the expression error in the do-resolve() action parser
When an expression is used for do-resolve(), an error may be reported.
Unfortunately it was scratched and replaced by the do-resolve() error,
leaving no chance to know exactly what was wrong. Let's report the
contents of the error when available. It will indicate identifiers that
are not found or invalid ranges or types being used.
Willy Tarreau [Fri, 15 May 2026 15:49:16 +0000 (17:49 +0200)]
CLEANUP: resolvers: properly initialize the sample in resolv_action_do_resolve()
The sample used to pass the IP address only had its data, px, sess and
strm fields initialized before being passed to vars_set_by_name(). It
turns out that this latter one doesn't seem to touch ctx, flags nor opt
but nothing guarantees it. Let's at least initialize the fields properly
to avoid passing random garbage.
Willy Tarreau [Fri, 15 May 2026 15:33:39 +0000 (17:33 +0200)]
BUG/MINOR: proxy: use proxy_drop() in parse_new_proxy() error path
In parse_new_proxy(), when proxy_defproxy_cpy() fails, the error path used
ha_free(&curproxy) to release the partially constructed proxy. However, the
proxy was allocated via alloc_new_proxy() which performs significant setup:
- setup_new_proxy() inserts it into the proxy_by_name tree (proxy_store_name)
- It appends to the global proxies list (LIST_APPEND)
- proxy_take() increments its refcount
Additionally, proxy_defproxy_cpy() may have allocated further resources
(strdup'd strings, compression structures, email alert fields, etc).
Using ha_free() only freed the proxy struct itself, leaving:
- The proxy still registered in the name tree (dangling pointer)
- The proxy still linked in the global proxies list
- All strdup'd strings and other allocations leaked
This is visible with ASAN when causing random allocation errors:
[NOTICE] (27033) : haproxy version is 3.4-dev12-b15468-11
[NOTICE] (27033) : path to executable is ./haproxy
[ALERT] (27033) : config : parsing [/dev/stdin:5015] : proxy 'bk3': failed to duplicate tcpcheck preset-vars
[ALERT] (27033) : config : Error(s) found in configuration file : /dev/stdin
Direct leak of 4 byte(s) in 1 object(s) allocated from:
#0 0x7f113e518e20 in strdup (/usr/lib64/libasan.so.8+0x118e20)
#1 0x000000955410 in setup_new_proxy src/proxy.c:3178
#2 0x000000955816 in alloc_new_proxy src/proxy.c:3221
#3 0x000000956c33 in parse_new_proxy src/proxy.c:3554
#4 0x000000a24d03 in cfg_parse_listen src/cfgparse-listen.c:495
#5 0x00000089d33e in parse_cfg src/cfgparse.c:2202
#6 0x0000009e0bb9 in read_cfg src/haproxy.c:1142
#7 0x000000447e8c in main src/haproxy.c:3474
#8 0x7f113d82ad13 in __libc_start_call_main (/lib64/libc.so.6+0x2ad13)
#9 0x7fff65b4e320 ([stack]+0x20320)
SUMMARY: AddressSanitizer: 4 byte(s) leaked in 1 allocation(s).
The fix replaces ha_free(&curproxy) with proxy_drop(curproxy), which
properly calls deinit_proxy() to release all internal resources, removes
the proxy from trees and lists, decrements the refcount, and frees the
struct.
No backport is needed since proxy_drop() is only in 3.4.
Willy Tarreau [Thu, 14 May 2026 20:56:52 +0000 (20:56 +0000)]
BUG/MINOR: dns: fix dangling dgram pointer on dns_dgram_init() failure path
In dns_dgram_init(), the newly created dgram is assigned to the name server
before the ring is attached. In case of errors, e.g. due to too many watchers,
the dgram is released but not removed from ns->dgram. Let's only assign the
pointer on success to avoid this, as it's not needed before. The problem
was introduced in 2.4 with commit c943799c86 ("MEDIUM: resolvers/dns: split
dns.c into dns.c and resolvers.c"), and was possibly there before. The fix
may be backported to all stable versions.
Willy Tarreau [Thu, 14 May 2026 22:59:58 +0000 (22:59 +0000)]
BUG/MINOR: resolvers: fix dangling list pointer in resolvers_new() error paths
The resolver 'r' is appended to the global sec_resolvers list, but upon failure
later, pointers are released but the element remains in the list, corrupting it,
and possibly causing a crash during deinit() when releasing remaining ones.
Adding a LIST_DEL_INIT() on the error unrolling path is sufficient.
Note that the issue will only happen on failure to allocate memory via
strdup() so the risk is low. The bug was introduced in 2.6 by commit e7f5776800 ("MINOR: resolvers: resolvers_new() create a resolvers with
default values"), so the fix may be backported to several releases, but
does not necessarily have to go that far.
Willy Tarreau [Fri, 15 May 2026 06:09:26 +0000 (06:09 +0000)]
BUG/MEDIUM: server/cli: unlock server lock on failure in cli_parse_set_server
In cli_parse_set_server()'s 'ssl' branch, the server lock is taken,
and not released in case srv_set_ssl() fails, resulting in a dead lock
and a panic the next time an attempt to touch this server is made. The
lock must be released on all error paths.
This was introduced in 3.3 by commit f8f94ffc9 ("BUG/MEDIUM: server:
Use sni as pool connection name for SSL server only") which was marked
for backporting to 3.0, so this must likely be backported that far.
Willy Tarreau [Fri, 15 May 2026 05:20:48 +0000 (05:20 +0000)]
BUG/MINOR: servers: use proper source of pool_conn_name in srv_settings_cpy()
The condition 'if (srv->pool_conn_name)' was checking the destination
instead of the source 'src->pool_conn_name', meaning the strdup() would
never fire (since newly calloc'd servers start with NULL pool_conn_name),
and the pool_conn_name setting from default-server was silently ignored.
Introduced in 3.2 with commit f0f1816f1 ("MINOR: check: implement
check-pool-conn-name srv keyword") when pool_conn_name support was added
to srv_settings_cpy(). The bug caused any 'pool-conn-name' setting in a
'default-server' line to be lost for all servers inheriting from it.
Note that it's not the first time this function induces such a bug due
to the poor choice of "srv" vs "src" that should be renamed to avoid
keyboard mistakes and visual confusion.
Willy Tarreau [Fri, 15 May 2026 14:55:07 +0000 (16:55 +0200)]
BUG/MINOR: server: better handling of OOM in srv_set_fqdn()
This function may face an OOM on strdup() in the middle of the hostname
or hostname_dn replacement, leaving NULLs in either or both of the server's
fields, which is definitely not good for other call places.
Let's perform a safe replacement instead: we first allocate the new
values, and only if they are successful, then we release the previous
ones and replace them.
It is not necessary to backport this unless the issue is reported (it
was found via code review).
Willy Tarreau [Thu, 14 May 2026 20:59:53 +0000 (20:59 +0000)]
CLEANUP: dns: fix misleading error messages in dns_stream_init()
All task allocation errors report "memory allocation error initializing
the ring" when the actual failure was task_new_anywhere() returning NULL.
This clearly is a copy-paste. Let's fix the error messages to help when
debugging. Since it's only about allocation failures during init, there
is probably no point in backporting this.
Willy Tarreau [Fri, 15 May 2026 08:38:18 +0000 (08:38 +0000)]
CLEANUP: proxy: fix tiny mistakes in parse error messages
One is s/keyworld/keyword in the retry-on parser. The other one is a
wrong argument "len" being printed in case of parse error for
"declare capture" instead of the length itself.
These can be backported though they are not important.
Willy Tarreau [Thu, 14 May 2026 20:58:46 +0000 (20:58 +0000)]
BUG/MEDIUM: dns: fix memory leak of sockaddr in dns_session_init() error path
In dns_session_init(), sockaddr_alloc() allocates 'addr' from the sockaddr
pool, but on failure of appctx_finalize_startup() we jump to the error label
without calling sockaddr_free(&addr), leaking the allocation. Let's add the
missing sockaddr_free() on the error branch.
Willy Tarreau [Thu, 14 May 2026 22:10:46 +0000 (22:10 +0000)]
BUG/MEDIUM: resolvers: fix name compression pointer validation in resolv_read_name()
The original DNS code would only use the 8 lower bits of the compression
offset. This was fixed in 2.0 with commit 2fa66c3b9 ("BUG/MEDIUM: dns:
overflowed dns name start position causing invalid dns error") but it was
not sufficient because the anti-loop check continues to use only 8 of the
14 bits, thus a crafted response where the 8 lower bits pass the check and
the 6 higher should fail it would be accepted. The impacts remains limited
thanks to the bounds check and the recursion limits, but such invalid
responses could still cost a lot to process. Let's compute the 14-bit
offset once for all and use it everywhere.
Willy Tarreau [Thu, 14 May 2026 22:16:35 +0000 (22:16 +0000)]
BUG/MEDIUM: dns: fix long loops in additional records parse on name failure
In resolv_validate_dns_response(), the additional records loop calls
resolv_read_name(). When it returns zero due to a bad response, the main
loop does a "continue" without making the "reader" pointer progress, so it
evaluates the exact same field again and again. Fortunately this is limited
by arcount which is 16 bits, but it means it can still iterate 65535 times
there, allocating and releasing an answer_record at each turn. Let's just
jump to the invalid_resp label that handles the cleaning. There was the
same pattern (without the allocation) with nscount a few lines above BTW.
These can possibly explain some situations where a high CPU usage observed
processing responses.
Seems like these were introduced in 2.2 with commit 37950c8d2
("BUG/MEDIUM: dns: improper parsing of aditional records")
Willy Tarreau [Thu, 14 May 2026 23:01:36 +0000 (23:01 +0000)]
BUG/MINOR: config/dns: properly fail on duplicate nameserver name detection
In cfg_parse_resolvers(), two duplicate name checks set err_code but lacked
'goto out', allowing execution to fall through and create the duplicate entry.
This would result in new resolvers and nameservers to be created after the
error was displayed, and a leak of the previous one. It's mostly harmless
since we're exiting after such errors. This can be backported if desired.
Willy Tarreau [Fri, 15 May 2026 05:14:45 +0000 (05:14 +0000)]
BUG/MINOR: backend: correct parameter value validation in get_server_ph_post()
In the inner while loop that validates each character of a POST parameter
value, the code checks *p via HTTP_IS_TOKEN() and HTTP_IS_LWS() instead
of *end, while the loop condition only advances "end", so only the first
character of each value is validated.
This means spaces or binary data embedded in parameter values after the
first character goes undetected. Fix by replacing both references to *p
with *end to properly scan through all characters as intended.
This bug was introduced in 1.5-dev20 by commit 98634f0c7 ("MEDIUM:
backend: Enhance hash-type directive with an algorithm options") so
the fix must be backported to all versions.
Willy Tarreau [Wed, 13 May 2026 15:22:12 +0000 (17:22 +0200)]
[RELEASE] Released version 3.4-dev12
Released version 3.4-dev12 with the following main changes :
- SCRIPTS: announce-release: add a link to the OpenTelemetry filter
- BUG/MEDIUM: servers: Only requeue servers if they are up
- MINOR: tinfo: store the number of committed extra streams in the tgroup
- MINOR: connection: add a function to calculate elastic streams limit
- MINOR: mux-h2: consider the elastic streams limit on frontend
- MINOR: lb: make LB initialization even more declarative
- BUG/MINOR: cfgparse-listen: do not emit extraneous line in rule order warnings
- CLEANUP: tree-wide: fix typos in non user-visible comments in 15 files
- CLEANUP: h1/htx: fix a few typos in warning, debug and trace messages
- BUG/MINOR: mux-h1: only check h1s if not NULL
- BUG/MINOR: http-fetch: fix smp_fetch_hdr_ip()'s handling of brackets for IPv6
- BUG/MINOR: http-fetch: make http_first_req() check for HTTP first
- BUG/MINOR: http-act: set-status() must check the response message, not the request
- BUG/MINOR: tools: fix memory leak in env_expand() error path
- BUG/MINOR: auth: free user groups on error paths in userlist_postinit()
- BUG/MINOR: uri-auth: avoid leaks on initialization error
- BUG/MINOR: cache: fix memory leak in parse_cache_rule error path
- BUG/MINOR: cfgcond: make KQUEUE check for GTUNE_USE_KQUEUE not GTUNE_USE_EPOLL
- BUG/MINOR: mqtt: connack parser returns MQTT_NEED_MORE_DATA on unknown property
- BUG/MINOR: mqtt: connect parser uses wrong bit field for TOPIC_ALIAS_MAXIMUM
- BUG/MINOR: mqtt: connack parser uses wrong bit for SUBSCRIPTION_IDENTIFIERS_AVAILABLE
- BUG/MINOR: mqtt: fix PUBLISH flags validation that want all bits to be set
- CLEANUP: http_htx: rename inner 'type' to 'ptype' to avoid variable shadowing
- CLEANUP: mux-h2: fix minor output debugging format issues
- CLEANUP: http-rules: fix a few '&' vs '&&' checks for clarity
- CLEANUP: auth: remove undeclared auth_resolve_groups() from auth.h
- CLEANUP: cache: remove redundant res_htx assignment in http_cache_io_handler()
- CLEANUP: channel: remove bogus and unused definition of channel_empty()
- CLEANUP: flt_http_comp: remove duplicate rate limit and CPU usage checks
- CLEANUP: mqtt: remove duplicate MQTT_FN_BIT_USER_PROPERTY in CONNECT fields
- BUG/MINOR: uri-auth: fix possible null-deref in latest fix for leaks
- BUG/MEDIUM: tasks: Keep the TASK_RUNNING flag until queued
- CLEANUP: mqtt: fix spelling of shared_subscription_available
- CLEANUP: regex: pre-initialize error variable in regex_comp() to calm analysis
- BUILD: compiler: fix redefinition of __nonstring
- CLEANUP: defaults: adjust MAX_THREADS multiplier number in comment
- CLEANUP: src/cpuset.c: fix missing return in functions returning int
- REGTESTS: Use ${tmpdir} instead of hardcoded /tmp/
- REGTESTS: Don't try to use real nameservers for testcases
- CLEANUP: tree-wide: fix typos in non user-visible comments in 3 more files
- MINOR: cli: improve forward compatibility for show fd
- DOC: management: document the <tgid>/<fd> form of show fd
- CLEANUP: tree-wide: fix more typos and outdated explanations in comments
- BUG/MEDIUM: dict: hold read lock while incrementing refcount in dict_insert
- BUG/MEDIUM: http-client: Only consume input buffer when hc one is empty
- BUG/MINOR: xprt_qstrm: fix conflicting prototype
- REORG: mux_quic: use newer qcm prefix for legacy qmux files
- MINOR: mux_quic: use qcm prefix for mux callbacks
- MINOR: mux_quic: use qcm prefix for mux functions
- MINOR: mux_quic: use qcm prefix for traces functions/structs
- MINOR: mux_quic: rename qstrm files to qmux
- MINOR: mux_quic: remove qstrm naming in QUIC MUX
- MINOR: connection: rename QMux related flags
- MINOR: xprt_qmux: use qmux instead of qstrm naming
- MINOR: trace: implement source alias
- MEDIUM: mux_quic: rename qmux traces to qcm
- MINOR: sample: add a generic reverse converter
- MINOR: sample: add a reverse_dom converter
- DOC: proxy-protocol: clarify UDP usage
- BUILD: 51d.c: cleanup, fix preprocessor ifdefs
- CLEANUP: tree-wide: fix typos in user-invisible files
- CLEANUP: htx: Adjust numbering of HTX blocks' types in the description
Egor Shestakov [Wed, 13 May 2026 06:58:34 +0000 (06:58 +0000)]
CLEANUP: htx: Adjust numbering of HTX blocks' types in the description
Support of pseudo-headers was removed as unused, but mention of it
in the description remains and disrupt the numbering in comment, which
can be confusing.
Kevin Ludwig [Wed, 13 May 2026 14:53:58 +0000 (16:53 +0200)]
DOC: proxy-protocol: clarify UDP usage
the proxy protocol spec didn't specify UDP and therefore most
implementations treat it as a TCP connection and re-use the last send
information for a ip/port pair.
Manu Nicolas [Mon, 27 Apr 2026 14:59:01 +0000 (14:59 +0000)]
MINOR: sample: add a reverse_dom converter
In domain-based routing and policy rules, suffix matching on hostnames is
often easier to express as a prefix match on reversed labels. A dedicated
converter makes this convenient with existing fetches and matchers.
This also has a performance benefit for large maps. Prefix string matches use
the prefix-tree index (PAT_MATCH_BEG with pat_idx_tree_pfx), while end matches
use the string-list index (PAT_MATCH_END with pat_idx_list_str), so
reversed-label lookups can avoid linear suffix scans.
This patch adds "reverse_dom", a string converter that reverses domain labels,
ignores one optional trailing dot on input, and rejects empty labels. It
intentionally leaves trailing-dot handling to the caller so configurations can
choose between exact matches, subdomain-only matches, or an explicit dotted
form built with "concat(.)" for prefix lookups.
The documentation is updated and a reg-test covers the converter itself, the
explicit dotted form for "map_beg()", and the subdomain-only "-m beg" case.
Manu Nicolas [Mon, 27 Apr 2026 14:57:58 +0000 (14:57 +0000)]
MINOR: sample: add a generic reverse converter
Some use cases benefit from reversing a string before passing it to other
converters or lookups. While reverse_dom addresses domain-specific label
reversal, a generic byte-wise string reversal remains useful on its own and can
also be combined with other converters such as concat().
A common lookup use case is turning a suffix match on the original string into
a prefix match on the reversed string. Prefix string matches use the
prefix-tree index (PAT_MATCH_BEG with pat_idx_tree_pfx), while end matches use
the string-list index (PAT_MATCH_END with pat_idx_list_str), so reversing
before map_beg can avoid linear suffix scans for large maps.
This patch adds a new string converter named "reverse". It reverses the input
string byte by byte and returns the resulting string unchanged otherwise. It
does not apply any domain-specific semantics or character-encoding semantics.
The documentation is updated and a reg-test is added to cover the basic
conversion as well as a simple composition with concat(.).
Amaury Denoyelle [Mon, 11 May 2026 08:17:58 +0000 (10:17 +0200)]
MEDIUM: mux_quic: rename qmux traces to qcm
This patch is part of a renaming affecting mux_quic and related files.
Naming "qcm" will become the new marker for common mux_quic stuffs,
shared both on QUIC and QMux protocols. The final objective is to limit
"qmux" naming for stuffs related to the new experimental protocol of the
same name.
The current patch renames mux_quic traces token to "qcm". This is the
only change with external visible impacts in the whole renaming series.
However, to preserve compatibility, trace source alias is defined with
the older name "qmux". This relies on the previous patch which
introduced "alias" new trace_source member.
Amaury Denoyelle [Wed, 13 May 2026 11:38:31 +0000 (13:38 +0200)]
MINOR: trace: implement source alias
Add a new "alias" member in trace_source structure. Its purpose is to be
an alternative to the member "name". This will be used in the next patch
to allow renaming of QUIC mux traces while preserving compatibility.
This new member is only used in trace_find_source() which is the helper
used to retrieve a trace source from its name.
Amaury Denoyelle [Mon, 11 May 2026 08:52:49 +0000 (10:52 +0200)]
MINOR: xprt_qmux: use qmux instead of qstrm naming
This is a follow-up on the QUIC MUX renaming process.
The current patch performs renaming in xprt_qmux layer. Older "qstrm"
identifier is replaced by the new name "qmux". Every remaining functions
and structures in xprt_qmux are changed. Outside effects are only
present in QUIC MUX which directly uses some of these functions.
Amaury Denoyelle [Mon, 11 May 2026 08:49:15 +0000 (10:49 +0200)]
MINOR: connection: rename QMux related flags
This is a follow-up on the QUIC MUX renaming process.
The current patch performs renaming of "qstrm" to "qmux" in connection
flags. These flags are only used in linked with the xprt_qmux layer.
This has an impact on every files which manipulates these flags, namely
backend, session and ssl_sock sources.
Also, internal xprt identifier is renamed from XPRT_QSTRM to XPRT_QMUX,
Amaury Denoyelle [Mon, 11 May 2026 08:39:19 +0000 (10:39 +0200)]
MINOR: mux_quic: remove qstrm naming in QUIC MUX
This is a follow-up on the QUIC MUX renaming process.
The current patch replaces "qstrm" naming with "qmux" in QUIC MUX source
file. Some members are impacted in qcc and qcs structures, as well as
some internal functions used for QMux receive/send. Internal mux_ops is
also rename to qmux_ops. This is not a breaking change as its externally
visible name was already set to "qmux" originally.
Amaury Denoyelle [Mon, 11 May 2026 08:27:01 +0000 (10:27 +0200)]
MINOR: mux_quic: rename qstrm files to qmux
This is a follow-up on the QUIC MUX renaming process. Now most of "qmux"
generic usages as been replaced in favor of "qcm" naming. The next part
of the renaming is to replace "qstrm" naming with "qmux" for stuffs
related to the new QMux protocol specifically.
This is first applied on filenames. As with the previous renaming,
Makefile and include statements are updated as well to prevent
compilation issues.
Amaury Denoyelle [Mon, 11 May 2026 08:05:12 +0000 (10:05 +0200)]
MINOR: mux_quic: use qcm prefix for traces functions/structs
This is a follow-up on the QUIC MUX renaming process.
This patch renames several definitions in QUIC MUX traces source code.
This is only an internal change. Trace source name is kept to "qmux" for
now, but will be changed in a dedicated patch.
Amaury Denoyelle [Mon, 11 May 2026 07:25:32 +0000 (09:25 +0200)]
MINOR: mux_quic: use qcm prefix for mux callbacks
This is a follow-up on the QUIC MUX renaming process.
The current patch renames all MUX functions used as stream ops
callbacks. Also, internally defined mux_ops is also renamed from
"qmux_ops" to "quic_ops". There is no breaking change as mux_ops name
field remain set to "QUIC".
Amaury Denoyelle [Mon, 11 May 2026 07:45:30 +0000 (09:45 +0200)]
REORG: mux_quic: use newer qcm prefix for legacy qmux files
This patch is the first one of the renaming serie, affecting the QUIC
MUX module. The objective is to remove older "qmux" naming which was
used as a generic identifier. Now it should be restricted to the QMux
experimental protocol. A new "qcm" naming will replace the generic
usage.
The current patch renames the files themselves. Token "qmux" is replaced
by the new "qcm" identifier. Makefile and include statements are
adjusted as required.
Amaury Denoyelle [Mon, 11 May 2026 08:27:20 +0000 (10:27 +0200)]
BUG/MINOR: xprt_qstrm: fix conflicting prototype
This patch adds the missing include of xprt_qstrm header into its
companion source file. This helped to detect an incoherence in the
xprt_qstrm_xfer_rxbuf() prototype which is now fixed.
Header files is also updated with mandatory include statements and
forward declaration.
BUG/MEDIUM: http-client: Only consume input buffer when hc one is empty
Since http-client applet uses its own buffers, it is possible to have data
stuck in the applet input buffer while the http-client response buffer is
full, preventing the applet to consume these data. If this happens on the
last part of the response payload, the upper stream can decide to shut the
applet. In this case, the applet using the http client will not be able to
retrieve these last data because they will never be move into the hc
response buffer.
The main reason for this bug is that, for now, the applets cannot survive
the upper stream unlike multiplexers. It could be a good improvement for the
3.5. However, some applets still uses the stream-connector and the upper
stream (peer and stat applets for instance). So it is not an easy task.
In the mean time, to fix the issue on stable branches, the http-client
applet now stops to consume data when the hc response buffer is not empty.
This way, the applet shut will be deferred. Data will be consumed when they
can be fully moved in the httpclient response buffer.
This patch should fix the issue #3366. It must be backported to 3.3.
Willy Tarreau [Wed, 13 May 2026 08:57:29 +0000 (10:57 +0200)]
BUG/MEDIUM: dict: hold read lock while incrementing refcount in dict_insert
In dict_insert(), the read lock on d->rwlock was released before
incrementing the entry's refcount. Between the RDUNLOCK and the
HA_ATOMIC_INC, another thread could call dict_entry_unref() to drop
the refcount to zero, acquire the write lock, delete the entry from
the tree, and free it. The subsequent HA_ATOMIC_INC would then be a
use-after-free on freed memory.
The fix moves the HA_ATOMIC_INC inside the read lock, matching the
pattern used in stick_table.c for identical refcount-then-unlock
sequences.
It can be backported to the branches where this is relevant.
Maxime Henrion [Fri, 8 May 2026 14:35:53 +0000 (10:35 -0400)]
DOC: management: document the <tgid>/<fd> form of show fd
Add the syntax description, including the wildcard forms and the
note that <tgid> is currently parsed but ignored pending future
support for per-thread-group fd tables.
Maxime Henrion [Fri, 8 May 2026 14:30:30 +0000 (10:30 -0400)]
MINOR: cli: improve forward compatibility for show fd
The "<tgid>/" and "/" wildcard forms previously produced no output.
This isn't a bug since they are new, but a script written for future
versions (where the slash form will gain per-thread-group semantics)
would not work the same on 3.4. Make them produce output by dropping
the redundant ctx->fd = -1 wildcard sentinel; also tighten tgid
validation to reject values <= 0.
REGTESTS: Don't try to use real nameservers for testcases
The test doesn't need a real nameserver and in a isolated, restricted
test environment it might not be able to reach one at all, like with a
network sandbox. So lets just use 127.0.0.1:53. Even if there is none,
that's not a problem for this particular test.
REGTESTS: Use ${tmpdir} instead of hardcoded /tmp/
Tests may be excuted in sandboxed or minimalistic / restricted
environments, so incosistencies might cause trouble, like missing
permissions. So lets use the tmpdir variable instead, so the user might
define some path
Ilia Shipitsin [Sat, 25 Apr 2026 11:04:48 +0000 (13:04 +0200)]
CLEANUP: src/cpuset.c: fix missing return in functions returning int
Cppcheck found the issue described in github #2124, which can cause these
errors if no CPUSET implementation is supported (and CPUSET_USE_ULONG is
not enabled):
src/cpuset.c:21:11: error: Found an exit path from function with non-void return type that has missing return statement [missingReturn]
src/cpuset.c:36:11: error: Found an exit path from function with non-void return type that has missing return statement [missingReturn]
src/cpuset.c:100:1: error: Found an exit path from function with non-void return type that has missing return statement [missingReturn]
src/cpuset.c:124:1: error: Found an exit path from function with non-void return type that has missing return statement [missingReturn]
src/cpuset.c:152:1: error: Found an exit path from function with non-void return type that has missing return statement [missingReturn]
src/cpuset.c:163:1: error: Found an exit path from function with non-void return type that has missing return statement [missingReturn]
Willy Tarreau [Tue, 12 May 2026 06:40:32 +0000 (08:40 +0200)]
BUILD: compiler: fix redefinition of __nonstring
Dmitry Sivachenko reported a build warning on FreeBSD -dev, where
__nonstring is apparently already defined. Let's guard our own
definition to avoid such issues. It could make sense to backport
this to recent stable versions which may soon be exposed to modern
compilers.
Willy Tarreau [Mon, 11 May 2026 15:29:56 +0000 (17:29 +0200)]
CLEANUP: regex: pre-initialize error variable in regex_comp() to calm analysis
In regex_comp(), the error variable is either a const char* (USE_PCRE)
or a a uchar[] (USE_PCRE2), and navigating through the ifdefs is quite a
mess, making it hard to figure if it's always properly initialized when
printing an error message. Let's just preset it to NULL to clarify what
comes from where.
BUG/MEDIUM: tasks: Keep the TASK_RUNNING flag until queued
In task_schedule(), it is not enough to get the TASK_RUNNING flag before
setting the expire field, we also have to keep it while queueing the
taks, otherwise the task may run in the meanwhile and set expire to 0,
triggering the BUG_ON() in __task_queue() again. So now, only drop the
running flag once it's done.
Willy Tarreau [Mon, 11 May 2026 14:33:00 +0000 (16:33 +0200)]
BUG/MINOR: uri-auth: fix possible null-deref in latest fix for leaks
Latest commit 2dfbc311a8 ("BUG/MINOR: uri-auth: avoid leaks on
initialization error") left a possible null-deref case which was
surprisingly only detected by certain compiler combinations. No
backport needed.
Willy Tarreau [Mon, 11 May 2026 13:31:16 +0000 (15:31 +0200)]
CLEANUP: flt_http_comp: remove duplicate rate limit and CPU usage checks
In comp_prepare_compress_request(), the compression rate limit and CPU
usage checks were duplicated. The first set runs before selecting the
algorithm, and the second set runs after. That's definitely a copy-paste
issue or a patch being applied twice. Let's just drop one.
Willy Tarreau [Mon, 11 May 2026 12:54:01 +0000 (14:54 +0200)]
CLEANUP: http-rules: fix a few '&' vs '&&' checks for clarity
In http_re{q,s}_get_intercept_rule(), there are two occurrences of '&'
being used instead of '&&' which fortunately work thanks to the tests
being negations (hence 0/1 on each branch). Let's fix that and take this
opportunity for adding explicit precedence in http_apply_redirect_rule().
Willy Tarreau [Mon, 11 May 2026 12:49:36 +0000 (14:49 +0200)]
CLEANUP: mux-h2: fix minor output debugging format issues
In h2_dump_h2s_info(), the tl.calls was being printed as signed instead
of unsigned, which is not correct but harmless (only used with "show
fd"). In the same function, we don't check if h2s->sd is valid while
dereferencing it. In practise it is valid since "show fd" is run under
thread isolation, but it's far from being obvious, and if conditions
would later change, we don't know it could be printed between h2s_new()
and h2s_frt_stream_new(). Finally in h2s_make_data() a wrong set of
H2_EV_RX_* flags were used instead of H2_EV_TX_* to emit traces.
Willy Tarreau [Mon, 11 May 2026 12:39:16 +0000 (14:39 +0200)]
CLEANUP: http_htx: rename inner 'type' to 'ptype' to avoid variable shadowing
In http_add_header() there are "type" variables of the same type at two
levels, which is a bit confusing. The inner one is for the "prev" block,
so let's rename it "ptype" by analogy with "pblk".
Willy Tarreau [Mon, 11 May 2026 13:38:58 +0000 (15:38 +0200)]
BUG/MINOR: mqtt: fix PUBLISH flags validation that want all bits to be set
The definition of the PUBLISH message type indicates that the LSB are
independent, but uses a value of 0xF that clearly shows an attempt to
use a mask instead, but it results in all messages not having all flags
set to be rejected. A sane approach would have been to check for a mask
and an expected value. Let's just add a special case for it in function
mqtt_read_fixed_hdr() since that's for a single message type.
Willy Tarreau [Mon, 11 May 2026 13:36:08 +0000 (15:36 +0200)]
BUG/MINOR: mqtt: connack parser uses wrong bit for SUBSCRIPTION_IDENTIFIERS_AVAILABLE
In mqtt_parse_connack(), the MQTT_PROP_SUBSCRIPTION_IDENTIFIERS_AVAILABLE
case was checking and setting MQTT_FN_BIT_SUBSCRIPTION_IDENTIFIER instead
of MQTT_FN_BIT_SUBSCRIPTION_IDENTIFIERS_AVAILABLE, due to a copy-paste
mistake. This can be backported where needed.
Willy Tarreau [Mon, 11 May 2026 13:34:15 +0000 (15:34 +0200)]
BUG/MINOR: mqtt: connect parser uses wrong bit field for TOPIC_ALIAS_MAXIMUM
In mqtt_parse_connect(), the MQTT_PROP_TOPIC_ALIAS_MAXIMUM case was checking
and setting MQTT_FN_BIT_TOPIC_ALIAS instead of MQTT_FN_BIT_TOPIC_ALIAS_MAXIMUM.
This means duplicate detection for Topic-Alias-Maximum property was using the
wrong bitmask, and the actual Topic-Alias-Maximum bit was never set, making
duplicate detection ineffective for this property. The CONNACK parser already
had this correct.
Willy Tarreau [Mon, 11 May 2026 13:32:34 +0000 (15:32 +0200)]
BUG/MINOR: mqtt: connack parser returns MQTT_NEED_MORE_DATA on unknown property
In mqtt_parse_connack(), the switch statement's default case for unknown
MQTT properties was using 'return 0' which returns MQTT_NEED_MORE_DATA.
This is misleading: an unknown property should be treated as an invalid
message (MQTT_INVALID_MESSAGE), like other functions do. This branches to
the "end" label without touching the preset return value instead. This can
be backported if needed.
Willy Tarreau [Mon, 11 May 2026 13:27:51 +0000 (15:27 +0200)]
BUG/MINOR: cfgcond: make KQUEUE check for GTUNE_USE_KQUEUE not GTUNE_USE_EPOLL
In cfg_eval_cond_enabled(), the "KQUEUE" option incorrectly checks
GTUNE_USE_EPOLL instead of GTUNE_USE_KQUEUE. This is a copy-paste bug
from the preceding EPOLL case. It can be backported though it's harmless.
Willy Tarreau [Mon, 11 May 2026 13:26:32 +0000 (15:26 +0200)]
BUG/MINOR: cache: fix memory leak in parse_cache_rule error path
When the filter config (fconf) allocation fails in parse_cache_rule,
the previously allocated cache_flt_conf (cconf) and its strdup'd name
string are not freed. The error path only freed cconf but not
cconf->c.name, causing a memory leak.
Willy Tarreau [Mon, 11 May 2026 13:23:18 +0000 (15:23 +0200)]
BUG/MINOR: uri-auth: avoid leaks on initialization error
When stats_add_scope() and stats_add_auth() fail to initialize a field,
they just leave a partially allocated and initialized structure behind
them that is leaked. The whole architecture doesn't provide clean
unrolling abilities since everything is shared and assigned unconditionally,
but let's at least release what was just allocated. The whole approach would
probably deserve being revisited if one day this becomes more dynamic.
Willy Tarreau [Mon, 11 May 2026 13:06:41 +0000 (15:06 +0200)]
BUG/MINOR: auth: free user groups on error paths in userlist_postinit()
In userlist_postinit(), when an error occurs (missing group, missing user, or
allocation failure), the function returned immediately without freeing the
auth_groups_list linked lists that were built for all users in the first loop.
Each user's curuser->u.groups pointed to these allocated nodes, which leaked
on every error path.
Fix by replacing direct returns with a goto to a centralized cleanup label
that frees all users' groups lists before returning the error. Also fix a
trailing double space in one error return statement while refactoring.
Note that the impact is very low since we're supposed to fail to boo after
such errors.
Willy Tarreau [Mon, 11 May 2026 13:04:19 +0000 (15:04 +0200)]
BUG/MINOR: tools: fix memory leak in env_expand() error path
When my_realloc2() fails in env_expand(), the code jumps to 'leave:' and
returns NULL, but the original input 'in' is never freed (it's only freed
at line 4919 in the success case). Given that callers typically pass it
the direct return of strdup(), it looks like it is expected to always be
freed. This can be backported everywhere.
Willy Tarreau [Mon, 11 May 2026 13:01:30 +0000 (15:01 +0200)]
BUG/MINOR: http-act: set-status() must check the response message, not the request
action_http_set_status() checks for soft rewrite on the request message
by mistake instead of the response message. This could possibly cause a
rewrite failure when soft rewrite is enabled since it will not be seen
there, though the impact is extremely low. It can be backported.
Willy Tarreau [Mon, 11 May 2026 12:58:45 +0000 (14:58 +0200)]
BUG/MINOR: http-fetch: make http_first_req() check for HTTP first
smp_fetch_http_first_req() reads ->txn.http->flags without first
checking if txn.http is properly allocated. In theory if called from
the wrong context it could crash, even though tests where it's called
from "tcp-request content" don't seem to have any effect. Let's fix
it regardless, at least to dissipate the doubt. It can be backported
everywhere.
Willy Tarreau [Mon, 11 May 2026 12:56:22 +0000 (14:56 +0200)]
BUG/MINOR: http-fetch: fix smp_fetch_hdr_ip()'s handling of brackets for IPv6
IPv6 addresses can be read enclosed in brackets, but the length of the
string is not checked before checking them. If by lack of luck, the
buffer is empty but already contains '[' in the first place, we'd read
the byte at position -1, possibly crashing (even though in practice it
will not since allocated blocks will be precedeed by the malloc meta-
data). At least it could make asan/valgrind unhappy.
Willy Tarreau [Mon, 11 May 2026 12:44:12 +0000 (14:44 +0200)]
BUG/MINOR: mux-h1: only check h1s if not NULL
Since we can emit glitches during an H2 upgrade, we no longer have a
guaranteed h1s, so _h1_report_glitch() must check h1s before
dereferencing it. No backport is needed as this arrived in 3.4-dev11
with commit 72fd357814 ("MEDIUM: mux-h1: Return an error on h2 upgrade
attempts if not allowed").
Willy Tarreau [Sun, 10 May 2026 17:39:25 +0000 (17:39 +0000)]
CLEANUP: h1/htx: fix a few typos in warning, debug and trace messages
Just a few minor user visible issues issues found in mux_h1 and http_htx
(traces, warnings and debug output). This may be backported though isn't
important at all.
Willy Tarreau [Mon, 11 May 2026 07:32:41 +0000 (09:32 +0200)]
BUG/MINOR: cfgparse-listen: do not emit extraneous line in rule order warnings
Some functions such as tcp_parse_tcp_req() are able to emit their own
warnings by relying on warnif_misplaced_*() which directly prints the
warning. However when doing so they still increment the warning counter
which makes cfg_parse_listen() try to emit it, except that what's in the
variable is NULL, so we end up with:
Maxime Henrion [Wed, 29 Apr 2026 17:56:34 +0000 (13:56 -0400)]
MINOR: lb: make LB initialization even more declarative
This lets lb_ops specify the conditions necessary to bind to this set of
ops. The condition is expressed as a list of mask and match fields on
the algorithm flags. This is then used in proxy_finalize() to locate the
lb_ops corresponding to the current configuration, by iterating over
the list of lb_ops structures. This list is implemented using the same
mechanisms used for configuration keywords: an INITCALL1 macro to a
registration function.
This also moves the lookup and property flags into the lb_ops structure
that were previously applied manually on a case by case basis.
Willy Tarreau [Sat, 9 May 2026 15:58:31 +0000 (17:58 +0200)]
MINOR: mux-h2: consider the elastic streams limit on frontend
Now the streams-elasticity limit applies to h2 frontend connections.
It allows to reduce the number of advertised streams based on the
number of concurrent connections.
Willy Tarreau [Sat, 9 May 2026 14:37:25 +0000 (16:37 +0200)]
MINOR: connection: add a function to calculate elastic streams limit
This adds a new tune.streams-elasticity parameter. This parameter
indicates, as a percentage, the average number of streams per connection
at full load. It is used to calculate limits of the number of streams to
advertise on new connections. 0 means that no such limit is set.
When a limit is set, the new function conn_calc_max_streams() determines
the optimal number of streams to allow on a connection. It will assign at
least the ratio of streams left to connections left, and at least a fair
share of what's left times the number of desired streams. It will always
ensure that each connection gets at least 1 stream, and everything beyond
this will be evenly distributed. For now the function is not used.
Willy Tarreau [Sat, 9 May 2026 14:19:54 +0000 (16:19 +0200)]
MINOR: tinfo: store the number of committed extra streams in the tgroup
In order to be able to enforce global streams limitations, we'll first
have to be able to account how many streams we promised to serve via
frontend muxes. We'll always need to support at least one stream, which
is why here we're only counting extra streams beyond the first one. It
also has the benefit of leaving H1 out of this, and save it from updating
a variable. Also in order to avoid an important update cost, we're storing
this value per thread group. For now only H2 is implemented, but QUIC
should follow shortly and should only count bidirectional streams.
BUG/MEDIUM: servers: Only requeue servers if they are up
In init_srv_requeue(), only attempt to run the tasklet if the server is
actually running, otherwise it will end up being queued a second time,
when the server is actually brought up, and that will lead to a
corrupted mt_list.
This can easily be reproduced by adding a dynamic server, as those start
disabled, and then enabling and disabling it a couple of times.
This should fix github issue #3360.
Willy Tarreau [Fri, 8 May 2026 03:22:55 +0000 (05:22 +0200)]
[RELEASE] Released version 3.4-dev11
Released version 3.4-dev11 with the following main changes :
- BUG/MEDIUM: acme: fix segfault on newOrder with empty authorizations
- BUG/MINOR: acme: skip auth/challenge steps when newOrder returns a certificate
- BUG/MINOR: sink: do not free existing sinks on allocation error
- CLEANUP: net_helper: fix incorrect const pointers in writev_n16()
- BUG/MINOR: vars: make parse_store() return error on var_set() failure
- BUG/MINOR: vars: don't store the variable twice with set-var-fmt
- BUG/MINOR: vars: only print first invalid char in fill_desc()
- BUG/MINOR: hpack: validate idx > 0 in hpack_valid_idx()
- MINOR: add an MPSC ring buffer implementation
- OPTIM: quic: rework the QUIC RX code
- MINOR: quic: store the DCID as an offset
- OPTIM: quic: reduce the size of struct quic_dgram
- BUG/MINOR: quic: handle cases where we don't have an address
- BUG/MEDIUM: cli: fix master CLI connection slot leak on client disconnect
- MEDIUM: mux-quic: extend shut to app proto layer
- MINOR: h3/hq_interop: implement stream reset on shut abort/kill-conn
- BUG/MINOR: acl: fix a possible arg corruption in smp_fetch_acl_parse()
- BUG/MINOR: map: do not leak a map descriptor on load error
- CLEANUP: map/cli: fix some map-related help messages
- BUG/MINOR: pattern: release the reference on failure to load from file
- CLEANUP: acl: remove duplicate test in parse_acl_expr() and unused variable
- CI: github: add DEBUG_STRICT=2 to ASAN jobs
- BUG/MINOR: quic: fix buffer overflow with sockaddr_in46
- BUG/MEDIUM: acme: fix stalled renewal when opportunistic DNS check fails
- BUG/MINOR: quic: fix trace crash on datagram receive
- MINOR: quic: fix trace spacing when datagram is displayed
- CLEANUP: mux-h2: remove the outdated condition to release h2c on timeout
- BUILD: add an EXTRA_MAKE option to build addons easily
- BUILD: otel: removed USE_OTEL, addon is now built via EXTRA_MAKE
- CLEANUP: otel: move opentelemetry outside haproxy sources
- BUG/MEDIUM: mux-h2: fix the body_len to check when parsing request trailers
- BUG/MAJOR: mux-h2: preset MSGF_BODY_CL on H2_SF_DATA_CLEN in h2c_dec_hdrs()
- DOC: otel: update the filter's status and URL in the docs
- DOC: acme: document missing acme-vars and provider-name keywords
- BUG/MINOR: dns: always validate the source address in responses
- BUG/MINOR: tcpcheck: Properly report error for http health-checks
- CLEANUP: resolvers: Remove duplicated line when resolvers proxy is initialized
- BUG/MINOR: resolvers: Free new requester on error when linking a resolution
- BUG/MINOR: resolvers: Fix lookup for a hostname in the state-file tree
- BUG/MINOR: resolvers: Free opts on parse error in resolv_parse_do_resolve()
- BUG/MAJOR: net_helper: also fix tcp_options_list for OOB write loop
- BUG/MEDIUM: ssl/sample: check output buffer size in aes_cbc_enc converter
- BUG/MAJOR: http-ana: fix private session retrieval on NTLM
- REGTESTS: add a regtest to validate various NTLM transitions
- BUG/MEDIUM: mworker/cli: fix user and operator permission via @@<pid> in master CLI
- BUG/MINOR: mworker/cli: check ci_insert() return value in pcli_parse_request()
- REGTESTS: http-messaging: always send RFC8441 client settings to use ext connect
- BUG/MINOR: h2: add decoding for :protocol in traces
- BUG/MINOR: mux-h2: condition the processing of 8441 extension to global setting
- MINOR: mux-h2: add a new message flag to indicate ext connect support
- BUG/MINOR: h2: only accept :protocol with extended CONNECT
- BUG/MINOR: acme: contact mail should be optional, don't pass ToS bool
- CLEANUP: http-fetch: Remove duplcated return statement in smp_fetch_stver()
- CLEANUP: http-fetch: Adjust smp_fetch_url32_src() comment
- CLEANUP: http-fetch: Fix indentation of sample_fetch_keywords
- BUG/MINOR: http_fetch: Check return values of unchecked buffer operations
- BUG/MINOR: http-fetch: Fix http_auth_bearer() when custom header is used
- BUG/MEDIUM: h1_htx: Remove reverved block on error during contig chunks parsing
- CLEANUP: haterm: Remove duplicated bloc to know if haterm must drain
- BUG/MINOR: haterm: Immediately report error when draining the request
- CLEANUP: haterm: Remove useless IS_HTX_SC() test
- BUG/MINOR: haterm: Fix a possible integer overflow on the request body length
- BUG/MEDIUM: haterm: Subscribe for receives until request was fully drained
- BUG/MINOR: haterm: Don't set HTX_FL_EOM flag on 100-Continue responses
- BUG/MEDIUM: haterm: Properly handle end of request and end of response
- BUG/MEDIUM: haterm: Properly handle client timeout
- BUG/MINOR: haterm: Fix condition to use direct data forwarding
- BUG/MINOR: haterm: Report a 400-bad-request error on receive error
- DEBUG: haterm: Add hstream flags in the trace messages
- MINOR: haterm: Remove now useless req_body field from hstream
- MINOR: mux_quic: reset stream after app shutdown for HTTP/0.9
- MINOR: mux_quic: do not perform unnecessary timeout handling on BE side
- BUG/MEDIUM: mux_quic: adjust qcc_is_dead() to account detached streams
- MINOR: mux_quic: simplify MUX_CTL_GET_NBSTRM
- MINOR: ssl: Export 'current_crtstore_name'
- MINOR: ssl: Factorize code from "new/set ssl cert" CLI command
- MINOR: ssl: Factorize ckch instance rebuild process
- MEDIUM: ssl: Refactorize "commit ssl cert"
- BUG/MINOR: ssl: Use the sequence number with kTLS and TLS 1.2
- BUG/MINOR: mux_quic: fix max stream ID reuse estimation
- MINOR: mux_quic: release BE conns if reuse definitely blocked
- BUG/MINOR: mux_quic: refresh timeout only if I/O performed
- MEDIUM: mux-h1: Return an error on h2 upgrade attempts if not allowed
- BUG/MEDIUM: mux-h2: Properly consume padding for DATA frames
- MEDIUM: tools: read_line_to_trash() handle empty files without \n
- MINOR: jws: support HMAC in jws_b64_protected(), make nonce optional
- MINOR: jws: introduce jws_b64_hmac_signature() function for HMAC signing
- MINOR: acme: implement EAB - external account binding
- MINOR: acme: allow specifying custom MAC alg for EAB
- REGTESTS: Fix h1_to_h2_upgrade.vtc to force h2 on first bind line
- MINOR: cli: allow specifying a tgid with show fd
- Revert "BUG/MEDIUM: cli: fix master CLI connection slot leak on client disconnect"
- BUILD: use Makefile.mk instead of Makefile.inc in EXTRA_MAKE
- Revert "BUG/MINOR: mux-h2: condition the processing of 8441 extension to global setting"
- BUG/MEDIUM: mux-h2: fix the detection of the ext connect support
- MINOR: jwe: Add option to enable/disable algorithms or encryption algorithms for jwt_decrypt
- MINOR: jwe: Disable 'RSA1_5' algorithm by default in jwt_decrypt converters
- BUG/MEDIUM: jwe: Fix jwt.decrypt_alg_list to work correctly
- BUG/MEDIUM: stick-table: properly check permissions on CLI's set/clear cmd
- DOC: acme: EAB is now supported
Willy Tarreau [Thu, 7 May 2026 16:31:40 +0000 (18:31 +0200)]
BUG/MEDIUM: stick-table: properly check permissions on CLI's set/clear cmd
The "set stick-table" CLI command's permissions are checked a bit too
late in the I/O handler, because the lookups performed at parsing time
can already cause an entry to be created at level "user" even though the
user does not have the permission to go further and to fill the data in.
Note that the impact remains pretty low since the entry is created without
data being touchable, and all within the table's settings (max entries,
expire etc). In addition it cannot even be used to periodically refresh
an entry and prevent it from expiring because only a creation is handled
at this point.
Let's add the check in cli_parse_table_req() so that these privileged
commands are entirely denied past the table lookup. This way it remains
possible to know that the table doesn't exist, like for the "show" command
but not more.
This should be backported to all stable branches, because the bug right
now cannot result in an accidental use (entries are not properly created
and deletion does not work).
Thanks to Omkhar Arasaratnam for finding and reporting this.
BUG/MEDIUM: jwe: Fix jwt.decrypt_alg_list to work correctly
Function jwe_parse_global_alg_enc_list() handles both
jwt.decrypt_alg_list and jwd.decrypt_enc_list, but to know which array
to use, between the algorithms and encoding arrays to use, it was
checking the string to see if it matched jwe.supported_algorithms, so it
was always considering we were dealing with encodings, and
jwt.decrypt_alg_list could not possibly work.
Fix that by checking the right string.
MINOR: jwe: Disable 'RSA1_5' algorithm by default in jwt_decrypt converters
In RFC8725, section 3.2, they suggest to "Avoid all RSA-PKCS1 v1.5
encryption algorithms" so this algorithm gets disabled by default.
Tokens having this "alg" won't be decrypted unless it is explicitly
reenabled thanks to 'jwt.decrypt_alg_list' global option.
Thanks to Omkhar Arasaratnam for raising our awareness about this!
MINOR: jwe: Add option to enable/disable algorithms or encryption algorithms for jwt_decrypt
Some users of the jwt_decrypt_XXX converters might want to reject JWT
tokens with a specific algorithm or encryption algorithm ("alg" or "enc"
field respectively) in order to avoid weak algorithms for instance.
This could be done from the configuration but would be tedious.
This patch adds the new 'jwt.decrypt_alg_list' and
'jwt.decrypt_enc_list' global options that can be used to define a
subset of accepted algorithms
Willy Tarreau [Thu, 7 May 2026 15:15:06 +0000 (17:15 +0200)]
BUG/MEDIUM: mux-h2: fix the detection of the ext connect support
As reported by Huangbin Zhan (@zhanhb) in github issue #3355, latest
commit 96f7ff4fdd ("MINOR: mux-h2: add a new message flag to indicate
ext connect support") was not correct and can break RFC8441-compliant
clients, as it did for them with a variant of Chrome 142.
The problem is that while RFC9113 says that new pseudo-headers are only
permitted with *negotiated* extensions, and RFC8441 doesn't indicate
whether or not SETTINGS_ENABLE_CONNECT_PROTOCOL is needed from clients,
it only says that clients know that servers support the extension when
seeing it in their settings and can use it, which seems to imply that
they don't need to send it to indicate their willingness to use it.
This also means that the server cannot know if a client is expected to
use it or not by default. It only know that a client is not allowed to
use it if the server didn't emit support mentioning it, which haproxy
can do using h2-workaround-bogus-websocket-clients.
Thus the fix proposed by @zhanhb is right, when presetting the flag for
the parser to indicate whether or not we're willing to accept RFC8441's
:protocol pseudo-header, we should:
- consider the received setting on the backend side (though the
pseudo-header is neither used nor supported there, but at least
we pass the info regarding the support of the extension)
- consider the configuration for the frontend (since it's the only
place where we can decide on support or not)
This patch does just that and reverts the accompanying changes to the
regtests that made them want to see the client's setting. It must be
backported to 2.6.
In the mean time, placing this option in the global section will force
the clients to downgrade to h1:
h2-workaround-bogus-websocket-clients
Many thanks again to @zhanbb this feedback and proposing a tested fix.
The protocol was not super clear on one point when compared to RFC9113
and our internal setting GTUNE_DISABLE_H2_WEBSOCKET. While RFC9113 says
that protocol extensions are negotiated, RFC8441 is only advertised by
the server, which thus doesn't know if the client supports it or not
until it faces it. In addition, GTUNE_DISABLE_H2_WEBSOCKET doesn't
apply to the protocol support as it name seems to imply, but to the
frontend only since the corresponding option is
"h2-workaround-bogus-websocket-clients". As such, haproxy should not
expect the client to advertise anything regarding the setting, and
should not consider the option when receiving the server's setting.
This needs to be backported to 2.6 where the commit above was
backported.
As reported by Alexander Stephan in issue #3351, it causes problems.
First, as seen in the issue, the "reload" operation, handled by an applet
local to the master process, is being interrupted by the timeout so that
the client never gets the result (though the timeout is applied). A fix
for this was found (ignore client-fin/server-fin on applets, as they make
no sense there), but it only hides a deeper problem. Indeed, issuing
"@1 debug dev delay 2000" still stops at 1s with an error, indicating
that commands are systematically being sent with a shutdown, and thus
that the server-fin always applies. This is a problem because it means
that any long command will now be interrupted after one second.
All of this needs to be put back into perspective before progressing
further on this issue, and the reason for sending the shutdown should
be reconsidered in the context of the current version, as it looks
like this was once necessary but no longer is.
In addition, the issue encountered by Alexander, of a frozen worker,
was essentially reported once in many years, so it's totally acceptable
to leave older versions unfixed and figure what's the best solution for
modern versions only.
Let's just revert to the pre-fix situation so as to avoid causing
breakage everywhere. This revert should be backported to all versions
(2.4 included).
REGTESTS: Fix h1_to_h2_upgrade.vtc to force h2 on first bind line
With VTEST, It seems possible to receive the H2 preface in 2 packets. So the
preface cannot be matched and the H1 to H2 upgrade is not performed as
expected. The script was fixed by forcing the H2 proto on the first bind
line.
The problem with the preface matching will be reviewed later.
Mia Kanashi [Wed, 6 May 2026 21:17:43 +0000 (00:17 +0300)]
MINOR: acme: allow specifying custom MAC alg for EAB
This implements configuration for custom mac alg in EAB.
I don't think there are any reasons to allow that TBH,
but it is something that exists in the spec.
Configuring EAB requires two parts: Key ID and MAC Key.
Key ID is an ASCII string that specifies the name of the record CA
should look up. MAC Key is a base64url encoded key that is used
for the sake of JWS signing, using HS256 or other algorithms.
They are the credentials so must be stored securely.
A thing about EAB is that it is required only during account creation
so it is unexpectedly complex to think about.
Some CAs provide EAB credential pair that is reused between
multiple account order requests, for example ZeroSSL, but others like
Google Trusted Services require an unique EAB credential for each new
account creation request.
There are a lot of ways config could be implemented, I decided to make
so that Key ID and MAC Key are stored in separate files on disk,
that decision was made because of the security concerns.
File based approach in particular works well with systemd credentials,
works well with systems that have config world readable, or immutable,
and is compatible with existing setups that specify credentials in a
file.
EAB is configured through options like this in an acme section:
Mia Kanashi [Wed, 6 May 2026 21:17:41 +0000 (00:17 +0300)]
MINOR: jws: introduce jws_b64_hmac_signature() function for HMAC signing
New jws_b64_hmac_signature() duplicates the same functionality as
jws_b64_signature(), but for the use case of HMAC signing.
Intended to be used for ACME EAB.
OpenSSL allows to use EVP_PKEY for HMAC functionality, so
jws_b64_signature() could be reused, but the problem is that although
isn't deprecated it was removed in BoringSSL, and was removed
(due to BoringSSL roots) but then readded back in AWS-LC, because of
"legacy clients" (citing them), for that reason alone I say that having
a dedicated function for hmac is better, HMAC() macro seems to be widely
supported unlike other ways of doing same thing. Another alternative
would be to use EVP_MD API, but it was introduced in OpenSSL 3.0,
so not as widely supported.
Mia Kanashi [Wed, 6 May 2026 21:17:40 +0000 (00:17 +0300)]
MINOR: jws: support HMAC in jws_b64_protected(), make nonce optional
This adds support for HMAC algorithms in jws_b64_protected(), but also
makes nonce field optional, because it isn't needed in some cases where
HMAC is used, primarily ACME EAB requires that nonce field must not
exist.
Mia Kanashi [Wed, 6 May 2026 21:17:39 +0000 (00:17 +0300)]
MEDIUM: tools: read_line_to_trash() handle empty files without \n
fgets() returns NULL when EOF is reached before newline, handle
that as a success for consistency, current behaviour is arguably a bug,
the API of fgets() is pretty weird after all so someone probably forgot.
BUG/MEDIUM: mux-h2: Properly consume padding for DATA frames
Since the commit 617592c9e ("MEDIUM: mux-h2: try to coalesce outgoing
WINDOW_UPDATE frames"), padding of DATA frames is no longer
consumed. Instead, this padidng is left in the demux buffer and used as the
header of the next frame. Because all bytes of the padding must be zero,
this lead to trigger a PROTOCOL_ERROR because haproxy erroneously thinks the
peer sent a DATA frame for the stream-id 0. It is true for a padding of 9
bytes or more, but similar issues may be exprienced with smaller padding.
Before the commit above, the padding was consumed in h2_process_demux to
restore the H2_CS_FRAME_H state at the end of the while loop processing
received frames.
However, it seems a bit strange to deal with the padding at this stage,
espcially because it is not obvious at all. So to fix the issue, the padding
is now consumed at the end of h2_frt_transfer_data(), inside "end_tranfer"
label. At the stage, we know all payload of the current DATA frame were
consumed and only the padding is still there, if any. We must only take care
to not consume more than available in the demux buffer. The padding may have
been partially received.
This patch should fix the issue #3354. It must be backported as far as 2.8.
MEDIUM: mux-h1: Return an error on h2 upgrade attempts if not allowed
If h1 to h2 upgrades are not allowed, a 405-method-not-allowed error is now
returned from the H1 multiplexer itself instead of dealing with "PRI *
HTTP/2.0\r\n\r\n" request as a normal request.
Before this kind of request was caught by the HTTP analyzers and a
400-bad-request was returned. This was added before the multiplexers era to
protect backend apps against unexpected H1 to H2 uprade on server side.
Now, it is possible to handle the error in the H1 multiplexer. One benefit
is to be able to increment the glichtes counters. However, the error is
still handled in HTTP analyzers to be sure to detect unwanted upgrades that
can be hidden in H2 or H3 requests.
There is a special case. TCP > H1 > H2 upgrades. In that case, a H1 stream
exist. So we must report an error to the upper layer too.
A reg-test script was added to validate the feature. In addition,
tcp_to_http_upgrade.vtc was updated accordingly.