#32845·openssl

QUIC peer max_udp_payload_size getter returns 1200 when the transport parameter is absent (openssl-4.1.0-alpha1)

Author: gustafnCreated Sep 16, 2026Updated Sep 16, 2026
Labelstriaged: bug

OpenSSL version

The issue is present in:

  • OpenSSL 4.1.0-alpha1, tag openssl-4.1.0-alpha1, commit f42705198e92b4b969f05c973fff405880c7e80e
  • OpenSSL master commit 3bdd4a655bd707f66a7ea620ffae88afc3e15fa7 from 2026-09-15

OpenSSL was built from upstream source rather than supplied by the operating system.

Summary

After completion of a QUIC handshake, SSL_get_feature_peer_request_uint() with SSL_VALUE_QUIC_UDP_PAYLOAD_SIZE_MAX returns 1200 when the peer omitted the max_udp_payload_size transport parameter.

RFC 9000 Section 18.2 specifies that the default value of an absent max_udp_payload_size parameter is 65527:

https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2

Therefore, the effective peer value exposed by the OpenSSL API should be 65527 when the parameter is absent.

Reproduction

After completing the QUIC handshake, obtain the peer value with:

c
uint64_t peer_max_udp_payload_size = 0;

if (!SSL_get_feature_peer_request_uint(
        conn,
        SSL_VALUE_QUIC_UDP_PAYLOAD_SIZE_MAX,
        &peer_max_udp_payload_size)) {
    /* handle error */
}

printf("%llu\n",
       (unsigned long long)peer_max_udp_payload_size);

Connect using an unmodified h2load/ngtcp2 client:

h2load --alpn-list=h3 \
       --connect-to=127.0.0.1:8445 \
       -n 1 -c 1 \
       https://localhost:8445/

ngtcp2 initializes its local max_udp_payload_size transport parameter to the RFC default of 65527. Since this is the default, ngtcp2 omits the parameter from the encoded transport parameters.

OpenSSL reports:

1200

If h2load is modified to set:

c
params.max_udp_payload_size = 1452;

ngtcp2 explicitly encodes the non-default value, and OpenSSL correctly reports:

1452

This indicates that explicitly transmitted values are parsed correctly, while the absent-parameter case uses the wrong default.

Current implementation

In ch_init(), OpenSSL initializes the transmit Maximum Datagram Payload Length to 1200 and copies the same value into rx_max_udp_payload_size:

https://github.com/openssl/openssl/blob/f42705198e92b4b969f05c973fff405880c7e80e/ssl/quic/quic_channel.c#L167-L173

While parsing peer transport parameters, rx_max_udp_payload_size is updated only when the parameter is present:

https://github.com/openssl/openssl/blob/f42705198e92b4b969f05c973fff405880c7e80e/ssl/quic/quic_channel.c#L1695-L1710

The peer-request getter returns this field directly:

https://github.com/openssl/openssl/blob/f42705198e92b4b969f05c973fff405880c7e80e/ssl/quic/quic_channel.c#L4185-L4187

The source already defines the RFC default separately:

c
#define QUIC_MAX_MAX_UDP_PAYLOAD_SIZE 65527
#define QUIC_DEFAULT_MAX_UDP_PAYLOAD_SIZE \
    QUIC_MAX_MAX_UDP_PAYLOAD_SIZE

https://github.com/openssl/openssl/blob/f42705198e92b4b969f05c973fff405880c7e80e/include/internal/quic_types.h#L99-L100

The current initialization therefore conflates OpenSSL's conservative 1200-byte transmit MDPL with the effective value of the peer's transport parameter.

Expected behavior

When the peer omits max_udp_payload_size, the peer-request getter should return 65527, the RFC-defined default.

This should not change OpenSSL's transmit behavior. The transmit MDPL may remain fixed at 1200.

A possible correction would be to initialize:

c
ch->rx_max_udp_payload_size =
    QUIC_DEFAULT_MAX_UDP_PAYLOAD_SIZE;

while continuing to initialize:

c
qtx_args.mdpl = QUIC_MIN_INITIAL_DGRAM_LEN;

A regression test should complete a handshake in which the peer omits max_udp_payload_size and verify that the peer-request getter returns 65527.

Related context

PR #29664 originally stated that OpenSSL would dynamically use the full peer-advertised payload range:

https://github.com/openssl/openssl/pull/29664

During review, that transmit-path behavior was deliberately removed because OpenSSL does not yet perform the necessary path-MTU discovery:

https://github.com/openssl/openssl/pull/29664#discussion_r2852280016

The author subsequently confirmed that the functionality had been removed:

https://github.com/openssl/openssl/pull/29664#discussion_r2854789323

This report does not request restoration of dynamic transmit sizing. It concerns only the value exposed by the peer transport-parameter API when the parameter is absent.