Memory allocation based on untrusted length in rust-websocket
Description
Rust-WebSocket is a WebSocket (RFC6455) library written in Rust. In versions prior to 0.26.5 untrusted websocket connections can cause an out-of-memory (OOM) process abort in a client or a server. The root cause of the issue is during dataframe parsing. Affected versions would allocate a buffer based on the declared dataframe size, which may come from an untrusted source. When Vec::with_capacity fails to allocate, the default Rust allocator will abort the current process, killing all threads. This affects only sync (non-Tokio) implementation. Async version also does not limit memory, but does not use with_capacity, so DoS can happen only when bytes for oversized dataframe or message actually got delivered by the attacker. The crashes are fixed in version 0.26.5 by imposing default dataframe size limits. Affected users are advised to update to this version. Users unable to upgrade are advised to filter websocket traffic externally or to only accept trusted traffic.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
websocketcrates.io | < 0.26.5 | 0.26.5 |
Affected products
1- Range: < 0.26.5
Patches
1cbf6e9983e83Implement dataframe and message size limits
2 files changed · +62 −2
src/receiver.rs+43 −2 modified@@ -14,6 +14,10 @@ use crate::ws; use crate::ws::receiver::Receiver as ReceiverTrait; use crate::ws::receiver::{DataFrameIterator, MessageIterator}; +const DEFAULT_MAX_DATAFRAME_SIZE : usize = 1024*1024*100; +const DEFAULT_MAX_MESSAGE_SIZE : usize = 1024*1024*200; +const MAX_DATAFRAMES_IN_ONE_MESSAGE: usize = 1024*1024; + /// This reader bundles an existing stream with a parsing algorithm. /// It is used by the client in its `.split()` function as the reading component. pub struct Reader<R> @@ -74,14 +78,33 @@ where pub struct Receiver { buffer: Vec<DataFrame>, mask: bool, + // u32s instead uf usizes to economize used memory by this struct + max_dataframe_size: u32, + max_message_size: u32, } impl Receiver { /// Create a new Receiver using the specified Reader. + /// + /// Uses built-in limits for dataframe and message sizes. pub fn new(mask: bool) -> Receiver { + Receiver::new_with_limits(mask, DEFAULT_MAX_DATAFRAME_SIZE, DEFAULT_MAX_MESSAGE_SIZE) + } + + /// Create a new Receiver using the specified Reader, with configurable limits + /// + /// Sizes should not be larger than `u32::MAX`. + /// + /// Note that `max_message_size` denotes message size where no new dataframes would be read, + /// so actual maximum message size is larger. + pub fn new_with_limits(mask: bool, max_dataframe_size: usize, max_message_size: usize) -> Receiver { + let max_dataframe_size: u32 = max_dataframe_size.min(u32::MAX as usize) as u32; + let max_message_size: u32 = max_message_size.min(u32::MAX as usize) as u32; Receiver { buffer: Vec::new(), mask, + max_dataframe_size, + max_message_size, } } } @@ -96,14 +119,15 @@ impl ws::Receiver for Receiver { where R: Read, { - DataFrame::read_dataframe(reader, self.mask) + DataFrame::read_dataframe_with_limit(reader, self.mask, self.max_dataframe_size as usize) } /// Returns the data frames that constitute one message. fn recv_message_dataframes<R>(&mut self, reader: &mut R) -> WebSocketResult<Vec<DataFrame>> where R: Read, { + let mut current_message_length : usize = self.buffer.iter().map(|x|x.data.len()).sum(); let mut finished = if self.buffer.is_empty() { let first = self.recv_dataframe(reader)?; @@ -114,6 +138,7 @@ impl ws::Receiver for Receiver { } let finished = first.finished; + current_message_length += first.data.len(); self.buffer.push(first); finished } else { @@ -126,7 +151,10 @@ impl ws::Receiver for Receiver { match next.opcode as u8 { // Continuation opcode - 0 => self.buffer.push(next), + 0 => { + current_message_length += next.data.len(); + self.buffer.push(next) + } // Control frame 8..=15 => { return Ok(vec![next]); @@ -138,6 +166,19 @@ impl ws::Receiver for Receiver { )); } } + + if !finished { + if self.buffer.len() >= MAX_DATAFRAMES_IN_ONE_MESSAGE { + return Err(WebSocketError::ProtocolError( + "Exceeded count of data frames in one WebSocket message", + )); + } + if current_message_length >= self.max_message_size as usize { + return Err(WebSocketError::ProtocolError( + "Exceeded maximum WebSocket message size", + )); + } + } } Ok(::std::mem::replace(&mut self.buffer, Vec::new()))
websocket-base/src/dataframe.rs+19 −0 modified@@ -96,6 +96,25 @@ impl DataFrame { DataFrame::read_dataframe_body(header, data, should_be_masked) } + + /// Reads a DataFrame from a Reader, or error out if header declares exceeding limit you specify + pub fn read_dataframe_with_limit<R>(reader: &mut R, should_be_masked: bool, limit: usize) -> WebSocketResult<Self> + where + R: Read, + { + let header = dfh::read_header(reader)?; + + if header.len > limit as u64 { + return Err(io::Error::new(io::ErrorKind::InvalidData, "exceeded DataFrame length limit").into()); + } + let mut data: Vec<u8> = Vec::with_capacity(header.len as usize); + let read = reader.take(header.len).read_to_end(&mut data)?; + if (read as u64) < header.len { + return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "incomplete payload").into()); + } + + DataFrame::read_dataframe_body(header, data, should_be_masked) + } } impl DataFrameable for DataFrame {
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
9- github.com/advisories/GHSA-qrjv-rf5q-qpxcghsaADVISORY
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/4V2EOOU5OLEHVMKAH6BALQXKDKIZRXCI/mitrevendor-advisoryx_refsource_FEDORA
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/HYPNCM4H4OFBIZI6XMJ2DUTS54FT2TWP/mitrevendor-advisoryx_refsource_FEDORA
- nvd.nist.gov/vuln/detail/CVE-2022-35922ghsaADVISORY
- github.com/websockets-rs/rust-websocket/commit/cbf6e9983e839d2ecad86de8cd1b3f20ed43390bghsax_refsource_MISCWEB
- github.com/websockets-rs/rust-websocket/security/advisories/GHSA-qrjv-rf5q-qpxcghsax_refsource_CONFIRMWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/4V2EOOU5OLEHVMKAH6BALQXKDKIZRXCIghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/HYPNCM4H4OFBIZI6XMJ2DUTS54FT2TWPghsaWEB
- rustsec.org/advisories/RUSTSEC-2022-0035.htmlghsaWEB
News mentions
0No linked articles in our index yet.