VYPR
Moderate severityNVD Advisory· Published Mar 12, 2024· Updated Aug 1, 2024

Unlimited resource allocation by QUIC CRYPTO frames flooding in quiche

CVE-2024-1765

Description

Cloudflare Quiche (through version 0.19.1/0.20.0) was affected by an unlimited resource allocation vulnerability causing rapid increase of memory usage of the system running quiche server or client. A remote attacker could take advantage of this vulnerability by repeatedly sending an unlimited number of 1-RTT CRYPTO frames after previously completing the QUIC handshake. Exploitation was possible for the duration of the connection which could be extended by the attacker. quiche 0.19.2 and 0.20.1 are the earliest versions containing the fix for this issue.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Cloudflare Quiche before 0.19.2/0.20.1 has an unlimited resource allocation vulnerability via excessive CRYPTO frames leading to memory exhaustion.

Vulnerability

Overview CVE-2024-1765 is an unlimited resource allocation vulnerability in Cloudflare's Quiche QUIC implementation (through versions 0.19.1 and 0.20.0) that causes rapid memory consumption. The bug resides in how Quiche processes 1-RTT CRYPTO frames after a QUIC handshake is complete. A remote attacker can exploit this by sending an unbounded number of these frames, each containing data with increasing offsets, forcing the server or client to allocate memory for buffering without any upper limit. [1][2]

Exploitation

Details Exploitation requires the attacker to first complete a QUIC handshake with the target (server or client) to establish a connection. After this, the attacker continuously sends 1-RTT CRYPTO frames with ever-increasing data offsets. Because Quiche did not enforce a maximum bufferable offset for the crypto stream, it would allocate memory for each new offset, leading to a rapid increase in memory usage. The attack can be sustained for the duration of the connection, which the attacker can artificially prolong. [2]

Impact

Successful exploitation results in uncontrolled memory consumption on the system running Quiche. This can cause the process to exhaust available memory, leading to a denial of service (DoS) condition. The vulnerability affects both quiche servers and clients, as the vulnerable code processes CRYPTO frames irrespective of the role. [1][2]

Mitigation

The vulnerability is fixed in Quiche versions 0.19.2 and 0.20.1. The fix introduces a constant MAX_CRYPTO_STREAM_OFFSET (set to 2^16 = 65536) and checks that the offset in incoming CRYPTO frames does not exceed this limit, returning a new error CryptoBufferExceeded if it does. Implementation details can be seen in the commits referenced. [3][4]

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
quichecrates.io
< 0.19.20.19.2
quichecrates.io
>= 0.20.0, < 0.20.10.20.1

Affected products

2

Patches

2
11dbf5461ab6

limit maximum offset that can be buffered in a crypto stream

https://github.com/cloudflare/quicheAlessandro GhediniJan 10, 2024via ghsa
2 files changed · +83 0
  • quiche/include/quiche.h+3 0 modified
    @@ -124,6 +124,9 @@ enum quiche_error {
     
         // Error in key update.
         QUICHE_ERR_KEY_UPDATE = -19,
    +
    +    // The peer sent more data in CRYPTO frames than we can buffer.
    +    QUICHE_ERR_CRYPTO_BUFFER_EXCEEDED = -20,
     };
     
     // Returns a human readable string with the quiche version number.
    
  • quiche/src/lib.rs+80 0 modified
    @@ -489,6 +489,9 @@ const MAX_PROBING_TIMEOUTS: usize = 3;
     // The default initial congestion window size in terms of packet count.
     const DEFAULT_INITIAL_CONGESTION_WINDOW_PACKETS: usize = 10;
     
    +// The maximum data offset that can be stored in a crypto stream.
    +const MAX_CRYPTO_STREAM_OFFSET: u64 = 1 << 16;
    +
     /// A specialized [`Result`] type for quiche operations.
     ///
     /// This type is used throughout quiche's public API for any operation that
    @@ -567,6 +570,9 @@ pub enum Error {
     
         /// Error in key update.
         KeyUpdate,
    +
    +    /// The peer sent more data in CRYPTO frames than we can buffer.
    +    CryptoBufferExceeded,
     }
     
     impl Error {
    @@ -581,6 +587,7 @@ impl Error {
                 Error::FinalSize => 0x6,
                 Error::IdLimit => 0x09,
                 Error::KeyUpdate => 0xe,
    +            Error::CryptoBufferExceeded => 0x0d,
                 _ => 0xa,
             }
         }
    @@ -607,6 +614,7 @@ impl Error {
                 Error::IdLimit => -17,
                 Error::OutOfIdentifiers => -18,
                 Error::KeyUpdate => -19,
    +            Error::CryptoBufferExceeded => -20,
             }
         }
     }
    @@ -6809,6 +6817,10 @@ impl Connection {
                 },
     
                 frame::Frame::Crypto { data } => {
    +                if data.max_off() >= MAX_CRYPTO_STREAM_OFFSET {
    +                    return Err(Error::CryptoBufferExceeded);
    +                }
    +
                     // Push the data to the stream so it can be re-ordered.
                     self.pkt_num_spaces[epoch].crypto_stream.recv.write(data)?;
     
    @@ -9208,6 +9220,74 @@ mod tests {
             assert!(pipe.server.is_closed());
         }
     
    +    #[test]
    +    fn crypto_limit() {
    +        let mut buf = [0; 65535];
    +
    +        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
    +        config
    +            .load_cert_chain_from_pem_file("examples/cert.crt")
    +            .unwrap();
    +        config
    +            .load_priv_key_from_pem_file("examples/cert.key")
    +            .unwrap();
    +        config
    +            .set_application_protos(&[b"proto1", b"proto2"])
    +            .unwrap();
    +        config.set_initial_max_data(30);
    +        config.set_initial_max_stream_data_bidi_local(15);
    +        config.set_initial_max_stream_data_bidi_remote(15);
    +        config.set_initial_max_streams_bidi(3);
    +        config.enable_early_data();
    +        config.verify_peer(false);
    +
    +        // Perform initial handshake.
    +        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
    +        assert_eq!(pipe.handshake(), Ok(()));
    +
    +        // Client send a 1-byte frame that starts from the crypto stream offset
    +        // limit.
    +        let frames = [frame::Frame::Crypto {
    +            data: stream::RangeBuf::from(b"a", MAX_CRYPTO_STREAM_OFFSET, false),
    +        }];
    +
    +        let pkt_type = packet::Type::Short;
    +
    +        let written =
    +            testing::encode_pkt(&mut pipe.client, pkt_type, &frames, &mut buf)
    +                .unwrap();
    +
    +        let active_path = pipe.server.paths.get_active().unwrap();
    +        let info = RecvInfo {
    +            to: active_path.local_addr(),
    +            from: active_path.peer_addr(),
    +        };
    +
    +        assert_eq!(
    +            pipe.server.recv(&mut buf[..written], info),
    +            Err(Error::CryptoBufferExceeded)
    +        );
    +
    +        let written = match pipe.server.send(&mut buf) {
    +            Ok((write, _)) => write,
    +
    +            Err(_) => unreachable!(),
    +        };
    +
    +        let frames =
    +            testing::decode_pkt(&mut pipe.client, &mut buf[..written]).unwrap();
    +        let mut iter = frames.iter();
    +
    +        assert_eq!(
    +            iter.next(),
    +            Some(&frame::Frame::ConnectionClose {
    +                error_code: 0x0d,
    +                frame_type: 0,
    +                reason: Vec::new(),
    +            })
    +        );
    +    }
    +
         #[test]
         fn limit_handshake_data() {
             let mut config = Config::new(PROTOCOL_VERSION).unwrap();
    
1017466c143f

limit maximum offset that can be buffered in a crypto stream

https://github.com/cloudflare/quicheAlessandro GhediniJan 10, 2024via ghsa
2 files changed · +83 0
  • quiche/include/quiche.h+3 0 modified
    @@ -124,6 +124,9 @@ enum quiche_error {
     
         // Error in key update.
         QUICHE_ERR_KEY_UPDATE = -19,
    +
    +    // The peer sent more data in CRYPTO frames than we can buffer.
    +    QUICHE_ERR_CRYPTO_BUFFER_EXCEEDED = -20,
     };
     
     // Returns a human readable string with the quiche version number.
    
  • quiche/src/lib.rs+80 0 modified
    @@ -489,6 +489,9 @@ const MAX_PROBING_TIMEOUTS: usize = 3;
     // The default initial congestion window size in terms of packet count.
     const DEFAULT_INITIAL_CONGESTION_WINDOW_PACKETS: usize = 10;
     
    +// The maximum data offset that can be stored in a crypto stream.
    +const MAX_CRYPTO_STREAM_OFFSET: u64 = 1 << 16;
    +
     /// A specialized [`Result`] type for quiche operations.
     ///
     /// This type is used throughout quiche's public API for any operation that
    @@ -567,6 +570,9 @@ pub enum Error {
     
         /// Error in key update.
         KeyUpdate,
    +
    +    /// The peer sent more data in CRYPTO frames than we can buffer.
    +    CryptoBufferExceeded,
     }
     
     impl Error {
    @@ -581,6 +587,7 @@ impl Error {
                 Error::FinalSize => 0x6,
                 Error::IdLimit => 0x09,
                 Error::KeyUpdate => 0xe,
    +            Error::CryptoBufferExceeded => 0x0d,
                 _ => 0xa,
             }
         }
    @@ -607,6 +614,7 @@ impl Error {
                 Error::IdLimit => -17,
                 Error::OutOfIdentifiers => -18,
                 Error::KeyUpdate => -19,
    +            Error::CryptoBufferExceeded => -20,
             }
         }
     }
    @@ -6809,6 +6817,10 @@ impl Connection {
                 },
     
                 frame::Frame::Crypto { data } => {
    +                if data.max_off() >= MAX_CRYPTO_STREAM_OFFSET {
    +                    return Err(Error::CryptoBufferExceeded);
    +                }
    +
                     // Push the data to the stream so it can be re-ordered.
                     self.pkt_num_spaces[epoch].crypto_stream.recv.write(data)?;
     
    @@ -9215,6 +9227,74 @@ mod tests {
             assert!(pipe.server.is_closed());
         }
     
    +    #[test]
    +    fn crypto_limit() {
    +        let mut buf = [0; 65535];
    +
    +        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
    +        config
    +            .load_cert_chain_from_pem_file("examples/cert.crt")
    +            .unwrap();
    +        config
    +            .load_priv_key_from_pem_file("examples/cert.key")
    +            .unwrap();
    +        config
    +            .set_application_protos(&[b"proto1", b"proto2"])
    +            .unwrap();
    +        config.set_initial_max_data(30);
    +        config.set_initial_max_stream_data_bidi_local(15);
    +        config.set_initial_max_stream_data_bidi_remote(15);
    +        config.set_initial_max_streams_bidi(3);
    +        config.enable_early_data();
    +        config.verify_peer(false);
    +
    +        // Perform initial handshake.
    +        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
    +        assert_eq!(pipe.handshake(), Ok(()));
    +
    +        // Client send a 1-byte frame that starts from the crypto stream offset
    +        // limit.
    +        let frames = [frame::Frame::Crypto {
    +            data: stream::RangeBuf::from(b"a", MAX_CRYPTO_STREAM_OFFSET, false),
    +        }];
    +
    +        let pkt_type = packet::Type::Short;
    +
    +        let written =
    +            testing::encode_pkt(&mut pipe.client, pkt_type, &frames, &mut buf)
    +                .unwrap();
    +
    +        let active_path = pipe.server.paths.get_active().unwrap();
    +        let info = RecvInfo {
    +            to: active_path.local_addr(),
    +            from: active_path.peer_addr(),
    +        };
    +
    +        assert_eq!(
    +            pipe.server.recv(&mut buf[..written], info),
    +            Err(Error::CryptoBufferExceeded)
    +        );
    +
    +        let written = match pipe.server.send(&mut buf) {
    +            Ok((write, _)) => write,
    +
    +            Err(_) => unreachable!(),
    +        };
    +
    +        let frames =
    +            testing::decode_pkt(&mut pipe.client, &mut buf[..written]).unwrap();
    +        let mut iter = frames.iter();
    +
    +        assert_eq!(
    +            iter.next(),
    +            Some(&frame::Frame::ConnectionClose {
    +                error_code: 0x0d,
    +                frame_type: 0,
    +                reason: Vec::new(),
    +            })
    +        );
    +    }
    +
         #[test]
         fn limit_handshake_data() {
             let mut config = Config::new(PROTOCOL_VERSION).unwrap();
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

7

News mentions

0

No linked articles in our index yet.