Hi,

HAProxy 3.3.17 was released on 2026/10/14. It added 61 new commits
after version 3.3.16.

This release is mostly made of backports of the fixes shipped in 3.4.7. It
also carries the same two important security fixes. The first one concerns
the HTTP/2 multiplexer, where a truncated PING frame could prevent the CPU
from going back to idle, and the second one the HTTP/1 parser, where a
chunked request with a too long chunk-size line could lead to the same
behavior. CVEs were requested for both.

The rest of the release fixes issues in the configuration parser, the CLI, the
syslog applet used by "log-forward", the stick-table key exchange over peers,
maps/ACLs, the multiplexers (H1, H2 and FCGI), the compression filter and
several sample converters, along with a few crashes on AWS-LC builds of the SSL
layer. This branch also receives a fix of its own: the internal random
generator, whose output could otherwise be predicted by observers, is now
hardened.

* mux-h2: A truncated PING frame could waste the CPU of spurious wakeups.

   An HTTP/2 PING frame cannot be entirely consumed until it is complete,
   and the presence of an incomplete frame is sufficient to cause a wakeup,
   preventing the CPU from going idle until the entire frame is received,
   and resulting in a 100% CPU measurement for as long as the peer keeps the
   connection open without completing the frame (or until "timeout
   http-request" expired, when one is set). The performance impact is
   moderate and depends on the ratio of such connections among all
   concurrent connections. Partial PING frames are now detected as short
   reads like the other frame types, so the tasklet goes back to sleep until
   the rest of the frame arrives. The handling of short reads was fixed in
   2.5, but PING was missed; it only became a problem in 3.1 when the
   tasklet wake-up logic changed. As a workaround on an older build, setting
   "timeout http-request" to a low value such as 1-5 seconds limits the CPU
   consumption.

* h1-htx: A too long chunk-size line waste the CPU of spurious wakeups.

  For chunked messages, when a chunk-size line was longer than the input
  buffer, the parser made no progress, waiting for the end of the line. So
  no data was consumed, the buffer was full and could not be extended: the
  same bytes were retried endlessly with no request ever processed on it,
  preventing, as for the H2 multiplexer issue, the CPU from going idle. Here
  too, the impact is moderate and depends on the ratio of such connections
  among all concurrent connections. A parsing error (HTTP 400) is now
  reported as soon as the parser determines that the line cannot possibly
  fit, using the same full-buffer detection that already exists for
  oversized headers and trailers. Reported with a fix proposal by Adam
  Crawford.

* tools: The random generator was hardened against state recovery.

  The internal random generator was consumed in pairs and its output was
  used directly, so enough of its internal state could be observed on
  moderately idle systems to predict the values it produces: the UUIDs used
  in logs and identifiers, the QUIC retry tokens, or the WebSocket keys
  HAProxy generates. An XXH64 mixing layer seeded with the boot secret is
  now applied to its output, which hides that state, for about 2% of the
  UUID throughput when single-threaded (not measurable with threads).

* cfgparse: Several configuration parsing issues were fixed.

  A backend section given extra arguments after its name (e.g. "backend b
  junk") was silently ignored when it was the last section of the file; it
  is now properly rejected with a clear message. The "email-alert" options
  now really check that a value was provided, the proxy "description"
  keyword no longer leaks the previous string when used twice, the global
  "description" allocation is checked, and "max-spread-checks" no longer
  stores an uninitialized value on a parse error. Also, setting "localpeer"
  in the global section while passing "-L" on the command line used to make
  HAProxy refuse to load its configuration as a fatal error; it is now a
  warning again as documented, and the name from "-L" wins, so instances
  sharing a configuration file start again. Finally, the list of suggested
  options used when one is mistyped was completed and fixed.

* cli: A command line filling the buffer exactly could loop forever.

  Since the pipelining refactor, the last command of a line is detected by
  comparing its end with the tail of an internal buffer. When a command line
  (including its final LF) was exactly "tune.bufsize" bytes long, the tail
  wrapped and the last command was never detected: its words were then
  re-parsed as new commands, the buffer length underflowed, and the CLI kept
  answering "Unknown command" errors, keeping a thread busy as long as the
  client read them. With DEBUG_STRICT=2, HAProxy crashed instead. Reported
  and fixed by Yeonggi Kim.

* log: Syslog sessions in "log-forward" sections never ended.

  The syslog applet stopped reading its input as soon as the buffer was
  empty, which was also where the client's shutdown was noticed. When a
  client closed the connection after its last message had been forwarded,
  the session stayed open forever. A "log-forward" section does not inherit
  "timeout client" from the defaults section, so without an explicit timeout
  nothing ever closed these sessions: they accumulated, and a soft-stop then
  waited for "hard-stop-after" (forever if it is not set). This also
  happened when a "ring" forwarded to a local "log-forward" section, causing
  the previous process to survive a reload. Sessions now end correctly on
  client close and are accounted in "cli_aborts" as before. Reported and
  fixed by Yeonggi Kim.

* stick-table: Oversized keys could corrupt memory when replicated to peers.

  The overhead and the stored data were not accounted for when building the
  key sent over the peers protocol, although the whole message must fit in
  one buffer. A key close to the buffer size could therefore crash the
  sender, or overwrite a few bytes past the end of the buffer: this is
  reachable by tracking a large body or by applying an expansion converter
  (b64dec, hex, ...) to a key, or more easily with binary keys, which are
  always sent fully padded. Keys are now sized against the available room:
  string keys are trimmed, and binary keys too large to fit are refused at
  configuration time with an explicit message. There is no protocol change,
  so mixed-version peers still interoperate; realistic configurations are
  unaffected. Reported with a reproducer by Dmitri Kaslov.

  A stick-table counter was also fixed: the number of trackers returned
  by "table_trackers()" no longer counts the lookup itself, in line with
  the other fetches.

* map/acl: A use-after-free could be triggered with maps and ACLs.

  When a lookup succeeded through the tree without filling the result (the
  case of ACL matches), the returned sample still pointed at data left over
  by an earlier lookup done on the same thread outside the normal path,
  e.g. by the CLI's "get map" command. If that element was then freed ("del
  map", "clear map", or the old generation purged after "commit map"), the
  next matching request read freed memory, leading to random behaviour or a
  worker crash. Configurations using both an ACL and a map loaded from a
  virtual file are concerned. The returned pattern is no longer touched when
  it is not filled.

* tcpcheck: A crash was possible with "tcp-check expect rstring".

  The rule could be evaluated while the check's input buffer was not yet
  allocated, passing a NULL buffer to the regular expression engine, which
  expects a writable one. Only builds using the libc regex were affected
  (PCRE/PCRE2 and the binary case return cleanly). Such a rule now simply
  reports no match, like the string and binary cases already did.

* listener: A stuck accept loop could trigger the watchdog.

  Listeners were woken up without checking whether "maxconn" had already
  been reached. With many thread groups, a group could dequeue listeners,
  fail to accept because another group had consumed the last slots, and
  remain stuck long enough to trip the watchdog.

* ssl: Cipher and curve restrictions of crt-list lines were ignored with
  AWS-LC, and a few crashes were fixed.

  On AWS-LC builds, the SSL object keeps its own copy of the cipher lists
  and curves taken at creation, and switching to the certificate's SSL_CTX
  during the handshake did not refresh them. As a result, the "ciphers",
  "ciphersuites", "curves" and "ecdhe" options of a crt-list line were
  silently ignored, and a client could negotiate a cipher or a curve that
  the certificate explicitly excluded, as long as the bind line allowed
  it. These settings are now reapplied after the context switch (OpenSSL
  builds were not affected).

  In addition, "dump ssl cert" could crash on AWS-LC when a certificate in
  crt-store had no private key. Two crashes in the SSL I/O callback could
  also be hit when traces were enabled and a connection was destroyed
  instead of being returned to an idle pool or its session. Finally, X25519
  and X448 are now reported under their standard IANA names in traces, and
  "curves2nid()" can resolve them; the old "ecdh_x25519"/"ecdh_x448"
  spellings are kept as aliases.

* muxes: Several crashes and connection-handling bugs were fixed in the H1,
  H2 and FCGI multiplexers.

  A connection upgraded from HTTP/1 to HTTP/2 (via the H2 connection
  preface) is only allowed to do so on its very first request, but the check
  was defeated when the preface of a later request arrived split across
  several reads: the connection could then be switched to H2 in the middle
  of its life, dropping the end of the previous response that was still
  pending in the output buffer. The first-request condition is now checked
  reliably.

  Other fixes in this area: when the H2 multiplexer could not allocate a
  buffer to receive a large request body (with "tune.buffers.limit" for
  instance), it could crash while switching buffers; the reverse HTTP
  gateway ("rhttp@") could crash when an outgoing H2 connection failed,
  because the failure was not reported to the caller that then used a freed
  connection (an experimental feature); the H2 and FCGI multiplexers could
  return a pointer to an already freed tasklet when a deferred shutdown was
  scheduled, and the H1 multiplexer passed an upgraded (thus no longer
  valid) connection to the traces when destroying a stream. Also, when an
  error page (400, 408, 500, 501) was mapped to an empty file or /dev/null,
  the H1 multiplexer failed to release its embryonic stream, so the
  connection stayed open until the client timeout instead of being closed
  immediately.

* h3/quic/hq-interop: Several protocol robustness issues were fixed.

  HTTP/3 frames whose header itself was truncated are now rejected with
  H3_FRAME_ERROR, closing the connection immediately instead of leaving the
  stream to time out. A peer that resets (RESET_STREAM) its QPACK encoder or
  decoder stream is now detected as a critical-stream violation, like a
  close with FIN already was. In the QUIC transport, a STREAM frame that
  sets FIN before the already-received offset is now rejected with
  FINAL_SIZE_ERROR even when the final size is not yet known. The HTTP/0.9
  parser no longer sets the end-of-message flag twice when the final FIN
  arrives in its own empty frame, which could trip an internal consistency
  check. Finally, the QUIC stack now implements the AEAD integrity limit
  required by RFC 9001 ยง6.6: failed packet authentications are counted per
  connection and the connection is closed with AEAD_LIMIT_REACHED once the
  limit of the cipher is exceeded (2^52 for AES-GCM, 2^36 for
  ChaCha20-Poly1305). This is a hardening measure with no practical impact.

* flt-http-comp: The compression filter could truncate a message.

  Since the filter produces a new payload and may hold data in its own
  context, the end-of-message flag now stays cleared on the HTX message
  until the compression is really complete, instead of being visible while
  data is still buffered in the filter. Otherwise a message with no input
  left and the EOM flag set was considered complete and the remaining
  compressed data could be dropped, truncating the response.

* http-ana: "option checkcache" now returns a 502 as documented.

  When "option checkcache" blocked a response, the status returned to the
  client was still the server's original one (usually 200) instead of the
  502 bad gateway that the documentation promises and that "http-response
  deny" produces; the error files are now used as well.

* sample/fetch: Several converters and fetches returned wrong results.

  Time converters ("ms_ltime", "ms_utime", "us_ltime", "us_utime") applied
  their offset in the wrong unit, "http_date()" did not zero-pad the
  sub-second part ("23 ms" printed as ".23", same as "23 us"), the JWT part
  queries failed without a path, "quic_enabled()" and "req.ssl_ec_ext"
  failed instead of returning false, "res.cook_cnt()" miscounted Set-Cookie
  attributes, TCP-mode fetches exposed the ":method"/":path" pseudo-headers,
  and "language" skipped values after a truncated q-factor. All were fixed.

* spoe/stconn: A few internal robustness fixes.

  A SPOE agent configuration file that defines no "spoe-message" section
  corrupted the agent's message list during parsing, which could break the
  agent's messages; the list is now spliced correctly. Streams caught in the
  middle of a connection retry could crash the process while being dumped by
  "show threads", the watchdog or "warn-blocked-traffic-after"; endpoints
  are now replaced instead of being briefly detached, so an observer can
  never see a stream without endpoint.

* doc: The documentation was corrected on several points.

  The "url_ip" and "url_port" fetches are now documented as failing when the
  authority part contains a host name rather than an address. The "hex2i"
  converter is documented as failing (returning nothing) on unparsable input
  instead of returning zero. "option checkcache" now mentions that responses
  carrying neither a validator nor freshness information are not examined
  and pass unfiltered. The diagnostic mode section explains that some
  diagnostic messages interact with "zero-warning" mode (and thus must only
  be used interactively). Finally, the "log-steps" parser error message now
  states the comma-separated syntax it actually expects.

Thanks everyone for your help on this release.

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 :
Amaury Denoyelle (7):
      BUG/MINOR: server: fix "del server" description
      BUG/MINOR: hq-interop: prevent double EOM on request with standalone FIN
      BUG/MINOR: ssl: fix trace crash on FE conn release in io cb
      BUG/MINOR: ssl: fix trace crash on BE conn release in io cb
      BUG/MINOR: mux_quic: extend FINAL_SIZE_ERROR detection on stream
      BUG/MINOR: h3: reject frames with truncated header
      BUG/MINOR: h3: reject RS/SS for QPACK uni streams

Aurelien DARRAGON (1):
      BUG/MINOR: log: fix erroneous error message in log-steps parser

Christopher Faulet (8):
      BUG/MEDIUM: flt-http-comp: Block HTX_FL_EOM flag till the end of 
compression
      BUG/MINOR: flt-http-comp: Rely on htx_set_eom() to restore EOM flag
      BUG/MINOR: spoe: Fix the message list splice with an empty list
      BUG/MEDIUM: mux-h1: also release the embryonic stream when the error is 
empty
      BUG/MINOR: http-fetch: don't report pseudo-headers in TCP mode
      BUG/MEDIUM: mux-h1: only accept the H2 preface on the first request
      REGTESTS: http-messaging: Fix http_fail_cnt_body to succeed with small 
timeouts
      BUG/MAJOR: h1-htx: report an error on a too long chunk-size line

Frederic Lecaille (3):
      MINOR: quic: implement the AEAD integrity limit
      BUG/MEDIUM: ssl: crt-list ciphers ignored with AWS-LC
      BUG/MEDIUM: ssl: crt-list curves ignored with AWS-LC

Lukas Tribus (1):
      MINOR: doc: elaborate on diagnostic mode behavior

Olivier Houchard (1):
      BUG/MEDIUM: listener: Consider maxconn before waking listeners

Remi Tricot-Le Breton (1):
      BUG/MINOR: ssl: Fix potential NULL-deref in "dump ssl cert" with AWS-LC

William Lallemand (3):
      BUG/MINOR: ssl: use standard names for X25519 and X448 groups
      REGTEST: ssl: require OpenSSL 1.1.1 for crt-list ciphersuites tests
      BUG/MINOR: cfgparse-global: only warn when "localpeer" is set together 
with -L

Willy Tarreau (32):
      BUG/MEDIUM: tools: insert an XXH64 layer on the PRNG output
      MINOR: stconn: prefer to replace endpoints rather than detaching them
      MINOR: stick-table: provide a function to estimate on-wire data size
      MINOR: stick-table: calculate the on-wire size of each key
      BUG/MEDIUM: stick-table: restrict key sizes to what fits in a buffer
      BUG/MEDIUM: pattern: don't dereference static_pattern's data when not 
filling it
      BUG/MINOR: http: do not consume the delimiter of a truncated q-factor
      BUG/MINOR: sample: apply the ms_/us_ time offsets in the input unit
      BUG/MINOR: sample: zero-pad the fractional part emitted by http_date()
      BUG/MINOR: sample: make quic_enabled() always succeed
      BUG/MINOR: http-ana: return a 502 when checkcache blocks a response
      BUG/MINOR: stick-table: do not count the lookup itself in table_trackers()
      BUG/MINOR: payload: always report a boolean from req.ssl_ec_ext
      BUG/MINOR: http-fetch: do not count Set-Cookie attributes as cookies
      BUG/MEDIUM: tcpcheck: do not run a regex on an unallocated buffer
      BUG/MINOR: sample: return the decoded JWT part when no path is given
      DOC: config: mention other responses not blocked by option checkcache
      DOC: config: fix doc for hex2i() converter on unparsable input
      DOC: config: mention that url_ip and url_port fail when passed a name
      BUG/MINOR: cfgparse-global: add missing gotos to error label in 
max-spread-checks
      BUG/MINOR: cfgparse-global: add missing calloc() check for the global 
description
      BUG/MINOR: cfgparse-listen: release the previous description of the proxy
      BUG/MINOR: cfgparse-listen: fix the argument checked by "email-alert"
      BUG/MINOR: cfgparse-listen: properly reject extra args in a backend 
section
      BUG/MINOR: proxy: also look for the closest option among cfg_opts3
      BUG/MINOR: cfgparse-listen: complete the list of options for the 
suggestions
      BUG/MEDIUM: mux-h2: don't return a freed tasklet from h2_deferred_shut()
      BUG/MEDIUM: mux-fcgi: don't return a freed tasklet from 
fcgi_deferred_shut()
      BUG/MEDIUM: mux-h1: don't pass an upgraded connection to traces in 
h1s_destroy()
      BUG/MEDIUM: mux-h2: report a dead rhttp connection in h2_wake()
      BUG/MEDIUM: mux-h2: fix a possible crash on rxbuf allocation failure
      BUG/MAJOR: mux-h2: properly handle short reads on PING frames

Yeonggi Kim (4):
      BUG/MEDIUM: cli: fix the last command detection on a full command line
      REGTESTS: cli: test a command line of exactly tune.bufsize bytes
      BUG/MEDIUM: log: do not ignore the peer's shutdown in the syslog applet
      REGTESTS: log: check that a log-forward TCP session ends on client close

--
Christopher Faulet



Reply via email to