fix(moq-net): bound frame size in create_frame/append_frame#1882
Conversation
GroupProducer::create_frame rejects a frame whose declared size exceeds MAX_FRAME_SIZE before produce() allocates its buffer, with append_frame as a backstop for direct callers. This guarantees the limit for local callers, not just the wire-decode paths, with no API break (both methods already return Result and FrameTooLarge is an existing variant). Extracted from #1870 so it can land independently of the moqsink rewrite. The breaking fallible-constructor follow-up is #1881 (dev). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Walkthrough
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches✨ Simplify code
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
rs/moq-net/src/model/group.rs (1)
454-467: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a direct
append_frameoversize-path assertion.The new test only exercises
create_frame; it doesn’t validate the direct-caller backstop added inappend_frame. Add a case that builds aFrameProducerwithsize = MAX_FRAME_SIZE + 1and callsappend_frameto lock this contract down.Proposed test extension
#[test] fn append_rejects_oversized_frame() { let mut producer = Group { sequence: 0 }.produce(); let err = producer.create_frame(Frame { size: MAX_FRAME_SIZE + 1, }); assert!( matches!(err, Err(Error::FrameTooLarge)), "a frame over the limit is rejected" ); // A frame at the limit is still accepted. assert!(producer.create_frame(Frame { size: MAX_FRAME_SIZE }).is_ok()); + + // Direct append callers are also rejected by the append backstop. + let direct = Frame { size: MAX_FRAME_SIZE + 1 }.produce(); + assert!(matches!( + producer.append_frame(direct), + Err(Error::FrameTooLarge) + )); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@rs/moq-net/src/model/group.rs` around lines 454 - 467, The test `append_rejects_oversized_frame` currently only validates the `create_frame` method path. Add an additional assertion within this test that directly invokes the `append_frame` method with a FrameProducer containing a frame where size equals MAX_FRAME_SIZE + 1, and verify that it correctly returns an error matching Error::FrameTooLarge. This ensures the backstop validation in the append_frame method itself is properly tested and locked down.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@rs/moq-net/src/model/group.rs`:
- Around line 454-467: The test `append_rejects_oversized_frame` currently only
validates the `create_frame` method path. Add an additional assertion within
this test that directly invokes the `append_frame` method with a FrameProducer
containing a frame where size equals MAX_FRAME_SIZE + 1, and verify that it
correctly returns an error matching Error::FrameTooLarge. This ensures the
backstop validation in the append_frame method itself is properly tested and
locked down.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 384c5eea-ae9f-4ed4-83e9-ce494068e4ce
📒 Files selected for processing (2)
rs/moq-net/src/model/frame.rsrs/moq-net/src/model/group.rs
Summary
Bound the declared frame size at the
GroupProducerlevel so an oversized frame is rejected beforeFrameProducer::newpreallocates its buffer.create_framerejectssize > MAX_FRAME_SIZEwithError::FrameTooLargebeforeproduce()allocates.append_framecarries the same check as a backstop for direct callers (the buffer is already allocated by then, but it keeps an oversized frame out of the group cache).MAX_FRAME_SIZEdoc updated to describe where the limit is enforced and the remainingFrameProducer::newgap.append_rejects_oversized_frame(over the limit rejected, at the limit accepted).This is non-breaking:
create_frame/append_framealready returnResult, andFrameTooLargeis an existing#[non_exhaustive]variant. It guarantees the limit for local callers, not just the wire-decode paths (which already checked).Relationship to other PRs
frame.rs/group.rshunks here are byte-identical to moq-gst: replace moqsink with a GstAggregator-based core #1870's, so once this lands moq-gst: replace moqsink with a GstAggregator-based core #1870 can rebase and those hunks drop out cleanly. moq-gst: replace moqsink with a GstAggregator-based core #1870 should drop its copy (or just rebase) to avoid a conflict.FrameProducer::newitself fallible (closing the direct-constructor gap, the true single chokepoint) is #1881, ondev. This PR is the non-breaking guard formain.Test plan
Run via
nix develop:just rs check(check, clippy-D warnings, fmt--check, doc-D warnings, shear, sort)cargo test -p moq-net— 347 unit + doctests pass, incl. newappend_rejects_oversized_framecargo test -p moq-native -p hang -p moq-mux -p moq-relay— all passCross-package sync
None needed: the wire format and public API signatures are unchanged.
(Written by Claude)