Hi,

HAProxy 3.3.14 was released on 2026/08/27. It added 96 new commits
after version 3.3.13.

This fixes a lot of issues, and a sizeable share of them were found
and reported with the help of AI-based tools/agents (Claude, OpenAI, and
a couple of other AI-assisted researchers), which seems to be an efficient way
to dig up this kind of corner case, so thanks to everyone involved for the
reports. A few areas received more attention than others and are worth
upgrading for:

  - CVE-2026-78120: a TLS session established on one "bind" of an HAProxy
    process could be resumed on a different "bind" of that same process,
    regardless of the certificate or the client-certificate verification policy
    configured on it.

    With stateful resumption (the shared session cache), this worked between
    any two binds of the process.

    With stateless resumption (tickets), it worked between any binds sharing
    the same tls-ticket-keys, even on a different process. By default,
    tls-ticket-keys are generated randomly per bind, so this doesn't apply
    unless the same keys are explicitly shared across binds.

    In practice this meant a session/ticket obtained on a bind with no
    client-certificate requirements could be resumed on another bind enforcing
    strict mutual TLS, without the client ever presenting a certificate on that
    second connection.

    Configurations that gate access by checking whether a certificate was
    actually used, rather than trusting the bind's handshake verification
    result, were not affected: ssl_c_used correctly reflects certificate usage
    on the current connection, whether resumed or not. As a mitigation without
    upgrading, this can be enforced in TCP mode:

      tcp-request content reject if ! { ssl_c_used }

    or in HTTP mode:

      http-request deny if ! { ssl_c_used }

    Alternatively, the stateful session cache can be disabled entirely, leaving
    stateless resumption -- whose tls-ticket-keys are randomly generated per
    bind by default -- as the only resumption mechanism:

      global
          tune.ssl.cachesize 0

    Note that, independently of this issue, it is good practice to inspect
    the certificate presented by the client, as explained on the wiki:
    
https://github.com/haproxy/wiki/wiki/mTLS-configuration-in-HAProxy#inspecting-the-client-certificate

    Three fixes now scope session/ticket resumption to the actual
    certificate, verification policy and crt-list filter in use.

  - tune.ssl.lifetime wasn't being enforced across TLS 1.3 session renewals
    (including on AWS-LC), letting a resumed/renewed session outlive the
    configured limit.

  - CVE-2026-78121: qpack_get_varint(), used to decode QPACK-encoded HTTP/3
    header fields, could shift a 64-bit value by 63 bits or more when parsing a
    varint with an excessive number of continuation bytes, which is undefined
    behavior in C. Impact is low: this was filed as BUG/MINOR since no
    exploitable consequence beyond the UB itself was identified on the
    compilers/platforms tested. The shift count is now explicitly bounded
    before use.

  - the "sink"/log forwarding code received 4 fixes: a "log tcp@..." line in
    the global section could crash at boot because its implicit forwarding
    server was left uninitialized; that same kind of implicit server, in an
    unrelated gap, also kept max-reuse disabled, causing a new TCP connection
    to be opened for every single log line instead of reusing it; a lock was
    needlessly held around ring_dispatch_messages(), which could deadlock; and
    a new implicit ring could get created for each copy of a logger, which was
    a waste of memory and could confuse "show events".

  - when a message's body was shorter than its announced Content-Length, the H1
    mux still reused the connection and the H2 mux still marked the message as
    complete, silently forwarding a truncated message and risking confusion
    with the next request on a reused connection; the H1 mux now closes the
    connection instead, and the H2 mux no longer marks the message as complete.

  - the cache code got 3 fixes: an entry could be released while still
    referenced under the cache read lock, a secondary entry could be retained
    without its primary one when detaching a row, and a request with a
    duplicated Origin or Referer header could match a secondary key computed
    from only the first occurrence, so such requests now bypass the cache
    instead of risking an unpredictable match.

  - a handful of correctness fixes: an absolute-form request-target with no
    path but a query string (e.g. "http://host?token=...";) had its query string
    swallowed into the authority, corrupting the request on the wire; rewriting
    a server-side cookie ("cookie ... rewrite" or "prefix") could silently fail
    to expand and still corrupt the byte following it in the response; a TCP
    payload filter could lose buffered data if the producer shut its connection
    while the filter still had pending data to flush; Lua's Channel:send()
    could resend the same prefix instead of the remainder after a partial
    write, duplicating data and desynchronizing the stream; HPACK could
    truncate H2 request methods/schemes longer than 127 bytes, which in
    practice mostly caused shared backend connections to be aborted with a
    protocol error under "http-reuse always"; and the "aes_gcm_dec" converter
    and the JWE AES-GCM key-unwrap path accepted a truncated authentication tag
    instead of requiring the full 16 bytes, which is now enforced.

  - "clear counters all" used to segfault the next request on any server,
    since it zeroed the shared per-thread-group counters pointers that every
    hot path dereferences without a NULL check; this also fixes "clear
    counters all" not actually resetting cumulative counters as documented.
    Thanks to Alexander Stephan for the patch.

On top of this, a new has_ctl() converter was added to detect control
characters in a sample.

Upgrading is recommended for everyone running 3.3, especially those relying on
client certificate authentication.

Please find the usual URLs below :
   Site index       : https://www.haproxy.org/
   Documentation    : https://docs.haproxy.org/
   Wiki             : https://github.com/haproxy/wiki/wiki
   Discourse        : https://discourse.haproxy.org/
   Slack channel    : https://slack.haproxy.org/
   Issue tracker    : https://github.com/haproxy/haproxy/issues
   Q&A from devs    : https://github.com/orgs/haproxy/discussions
   Sources          : https://www.haproxy.org/download/3.3/src/
   Git repository   : https://git.haproxy.org/git/haproxy-3.3.git/
   Git Web browsing : https://git.haproxy.org/?p=haproxy-3.3.git
   Changelog        : https://www.haproxy.org/download/3.3/src/CHANGELOG
   Dataplane API    : 
https://github.com/haproxytech/dataplaneapi/releases/latest
   Pending bugs     : https://www.haproxy.org/l/pending-bugs
   Reviewed bugs    : https://www.haproxy.org/l/reviewed-bugs
   Code reports     : https://www.haproxy.org/l/code-reports
   Latest builds    : https://www.haproxy.org/l/dev-packages


---
Complete changelog :
Alexander Stephan (1):
      BUG/MEDIUM: counters: preserve shared.tg pointer on 'clear counters all'

Amaury Denoyelle (4):
      BUG/MINOR: server: check strdup return value on server ID
      BUG/MINOR: proxy: fix default-server leak on post-parsing cleanup
      BUG/MEDIUM: quic: prevent out-of-bound read on wrapping CRYPTO content
      BUG/MINOR: quic: drop multiple Retry on same connection

Aurelien DARRAGON (5):
      MINOR: log/tools: fix ambiguous comments for some log encoding helpers
      BUG/MEDIUM: log: always reserve room for trailing 0 when using CBOR 
encoding helpers
      BUG/MEDIUM: lua: resume Channel:send() from the unsent part of the string
      BUG/MEDIUM: hlua_fcn: ensure systematic bref cleanup for patref list 
iterator
      BUG/MEDIUM: stats-file: fix shm-stats-file recover when all process slots 
are full

Austin Kauffman (1):
      BUG/MINOR: fcgi-app: allow explicit filter declaration with 
non-cache/non-compression filters

Christopher Faulet (6):
      BUG/MEDIUM: filter: Disable auto-close on channel during TCP payload 
filtering
      BUG/MINOR: config: Check buffer pool creation for failures
      BUG/MINOR: hlua: Properly enable/disable receives for TCP applets
      BUG/MINOR: tcpcheck: Don't release ruleset when parsing 'spop-check' 
ruleset
      BUG/MEDIUM: tools: make string encoding possible to fail instead of 
truncating
      BUG/MINOR: flt-http-comp: Don't read next block to detect end of data

Frederic Lecaille (2):
      BUG/MINOR: server: fix off-by-one error when parsing and copying source 
port range
      BUG/MINOR: qpack: missing shift count check in qpack_get_varint() (UB)

Kirill Furman (1):
      BUG/MINOR: log: fix double-free error when error in parse_loger occurs

Mani Goyal (1):
      BUG/MEDIUM: http: fix authority parsing for absolute-form URI with empty 
path

Manu Nicolas (1):
      BUG/MINOR: resolvers: accept fields at the response boundary

Miroslav Zagorac (1):
      BUG/MINOR: ot: removed dead code in flt_ot_parse_cfg_str()

Olivier Houchard (12):
      BUG/MINOR: cli: use the current argument to parse the FD spec in "show fd"
      BUG/MEDIUM: spoe: clear the applet pointer when the applet fails to start
      BUG/MINOR: mux-fcgi: don't call fcgi_strm_destroy() on a NULL stream
      BUG/MEDIUM: http-ana: don't crash on "keep-query" in a response redirect
      BUG/MEDIUM: stick-tables: use the same bucket for string keys with a NUL
      BUG/MEDIUM: sock: bound the recvmsg() length when receiving old sockets
      BUG/MEDIUM: http-ana: check the cookie rewrite result before moving the 
offsets
      BUG/MINOR: mux-fcgi: sanitize the STDERR records before logging them
      BUG/MEDIUM: bwlim: fix a stick-table entry leak in shared mode
      BUG/MEDIUM: mux-fcgi: check the room left before appending the index
      BUG/MEDIUM: mux-h1: close the connection on a short content-length
      MEDIUM: mux-h2: don't report EOM on a short content-length message

Remi Tricot-Le Breton (8):
      BUG/MINOR: jwt: don't take an extra reference on the certificate public 
key
      BUG/MEDIUM: ssl: require a full-length AEAD tag when decrypting with 
AES-GCM
      BUG/MINOR: conn: Do not check 'sess_el' list on frontend connections in 
__trace_enabled
      BUG/MINOR: ssl: Fix leak of X509_NAME in traces
      BUG/MINOR: ssl: release the previous client cert reference at depth > 0
      BUG/MEDIUM: ssl: Fix unprotected 'ssl_sock_choose_sni_ctx' calls
      BUG/MINOR: jwt: Missing 'jwt_tokenize' return value check
      MINOR: jwt: Improve 'jwt_tokenize' function

Rémi Tricot-Le Breton (2):
      BUG/MEDIUM: cache: retain the primary or secondary entry only when 
detaching its row
      BUG/MEDIUM: cache: do not release an entry under the cache read lock

William Lallemand (22):
      BUG/MINOR: ech: propagate error from load_echkeys()
      BUG/MINOR: ech: reject an ECH store with no usable private key
      BUG/MEDIUM: acme: don't delete a NULL token from the map
      BUG/MINOR: ssl: reject server certificate names containing a NUL byte
      BUG/MINOR: acme: restrict the permissions of the generated account key
      DOC: config: clarify req.ssl_sni
      BUG/MINOR: ssl: reject an embedded NUL in the ssl_*_dn(entry) fetches
      BUG/MINOR: ssl: reject an embedded NUL in the full-DN ssl_*_dn() fetches
      BUG/MINOR: payload: fix handshake length off-by-4 in ssl_hello_sni/alpn
      BUG/MINOR: spoe: check snprintf() return value in 
spoe_set_var/spoe_unset_var
      BUG/MEDIUM: ssl: enforce tune.ssl.lifetime across TLS1.3 session renewals
      BUG/MINOR: ssl: apply tune.ssl.lifetime to TLS1.3 sessions on 
BoringSSL/AWS-LC
      BUILD: ssl: disable the TLS1.3 session timeout clamp on wolfSSL
      BUG/MEDIUM: ssl: isolate TLS session resumption per X509 server 
certificate
      BUG/MINOR: ssl: isolate TLS session resumption per crt-list filter
      BUG/MEDIUM: ssl: isolate TLS session resumption per authentication policy
      BUILD: ssl: avoid a potential null-dereference warning on OpenSSL 1.0.2
      BUG/MINOR: ssl/cli: fix frontend-not-found detection in 'show ssl sni -f'
      BUG/MINOR: ech: fix label at end of compound statement
      Revert "BUG/MINOR: jwt: Missing 'jwt_tokenize' return value check"
      BUG/MINOR: admin: haproxy-reload use explicit socat address type
      BUG/MINOR: acme: NULL check on my_strndup()

Willy Tarreau (28):
      BUG/MEDIUM: hpack: encode long methods and schemes using the long form
      BUG/MINOR: stats-file: reject tgid 0 when preloading shm objects
      BUG/MINOR: hlua: use a local buffer to format the socket addresses
      BUG/MINOR: connection: reserve the whole CRC32C TLV before saving its 
pointer
      BUG/MEDIUM: session: don't release a reversed connection twice on error
      BUG/MINOR: lb-chash: bound the walk when the saved cursor changed tree
      BUG/MINOR: debug: only dump the trace once in __BUG_ON_ONCE()
      BUG/MINOR: mux-h2: strip the userinfo when deriving :authority for a 
server
      BUG/MINOR: quic: avoid a division by zero in the BBR pacing interval
      BUG/MEDIUM: sink: pre-initialize the implicit log forwarding server
      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: 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
      BUG/MINOR: mux-h2: harden h2_dump_h2s_info() against potentially null 
h2s->sd
      BUG/MEDIUM: connection: fix an infinite loop in the fc_pp_tlv() fetch
      BUG/MEDIUM: sink: do not hold the sft lock around ring_dispatch_messages()
      MINOR: sample: add new converter has_ctl() to detect control characters
      REGTESTS: converters: use connection: close in the has_ctl() test
      BUG/MEDIUM: cache: ignore cache on redundant origin/referer
      BUG/MEDIUM: sink: initialize the settings of the implicit log server
      BUG/MEDIUM: sink: do not create one implicit ring per logger copy

scientiamobile (1):
      BUG/MINOR: wurfl: fix memory leak of information list and patch strings 
at deinit

---

-- 
William Lallemand


Reply via email to