Skip to content

Commit efbe468

Browse files
kixelatedclaude
andauthored
moq-net: cap frame size at 16 MiB on the receive path (#1521)
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 1092fe3 commit efbe468

4 files changed

Lines changed: 25 additions & 3 deletions

File tree

rs/moq-net/src/error.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ pub enum Error {
8686
#[error("cache full")]
8787
CacheFull,
8888

89+
/// A frame declared a payload size larger than the receiver accepts.
90+
#[error("frame too large")]
91+
FrameTooLarge,
92+
8993
/// A remote error received via a stream/session reset code.
9094
#[error("remote error: code={0}")]
9195
Remote(u32),
@@ -118,6 +122,7 @@ impl Error {
118122
Self::Dropped => 24,
119123
Self::Closed => 25,
120124
Self::CacheFull => 26,
125+
Self::FrameTooLarge => 27,
121126
Self::App(app) => *app as u32 + 64,
122127
Self::Remote(code) => *code,
123128
}

rs/moq-net/src/ietf/subscriber.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use std::collections::{HashMap, hash_map::Entry};
22

33
use crate::{
4-
Broadcast, BroadcastDynamic, Error, Frame, FrameProducer, Group, GroupProducer, OriginProducer, Path, PathOwned,
5-
Track, TrackProducer,
4+
Broadcast, BroadcastDynamic, Error, Frame, FrameProducer, Group, GroupProducer, MAX_FRAME_SIZE, OriginProducer,
5+
Path, PathOwned, Track, TrackProducer,
66
coding::{Reader, Stream},
77
ietf::{self, Control, FilterType, GroupOrder, RequestId},
88
model::BroadcastProducer,
@@ -744,6 +744,9 @@ impl<S: web_transport_trait::Session> Subscriber<S> {
744744
return Err(Error::Unsupported);
745745
}
746746
} else {
747+
if size > MAX_FRAME_SIZE {
748+
return Err(Error::FrameTooLarge);
749+
}
747750
let mut frame = producer.create_frame(Frame { size })?;
748751

749752
if let Err(err) = self.run_frame(stream, frame.clone()).await {

rs/moq-net/src/lite/subscriber.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use futures::{StreamExt, stream::FuturesUnordered};
77

88
use crate::{
99
AsPath, BandwidthProducer, Broadcast, BroadcastDynamic, Error, Frame, FrameProducer, Group, GroupProducer,
10-
OriginProducer, Path, PathOwned, StatsHandle, SubscriberStats, SubscriberTrack, TrackProducer,
10+
MAX_FRAME_SIZE, OriginProducer, Path, PathOwned, StatsHandle, SubscriberStats, SubscriberTrack, TrackProducer,
1111
coding::{Reader, Stream},
1212
lite,
1313
model::BroadcastProducer,
@@ -457,6 +457,9 @@ impl<S: web_transport_trait::Session> Subscriber<S> {
457457
track_stats: Arc<SubscriberTrack>,
458458
) -> Result<(), Error> {
459459
while let Some(size) = stream.decode_maybe::<u64>().await? {
460+
if size > MAX_FRAME_SIZE {
461+
return Err(Error::FrameTooLarge);
462+
}
460463
let mut frame = group.create_frame(Frame { size })?;
461464
track_stats.frame();
462465

rs/moq-net/src/model/frame.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,17 @@ use bytes::{BufMut, Bytes};
77

88
use crate::{Error, Result};
99

10+
/// Maximum payload size accepted for a single frame on the wire.
11+
///
12+
/// The receive path preallocates a buffer from the declared frame size, so an
13+
/// untrusted peer could otherwise request a multi-gigabyte allocation with a
14+
/// single varint. Subscribers reject frames whose declared size exceeds this.
15+
///
16+
// TODO enforce this in [Frame::produce] / [FrameProducer::new] so the limit is
17+
// guaranteed for every caller, not just the wire decode paths. Blocked on
18+
// making the constructor fallible (returning [Result]), which is an API break.
19+
pub(crate) const MAX_FRAME_SIZE: u64 = 16 * 1024 * 1024;
20+
1021
/// A chunk of data with an upfront size.
1122
///
1223
/// Note that this is just the header.

0 commit comments

Comments
 (0)