From 8f34724b5fae103e265a59fcf07584256951a611 Mon Sep 17 00:00:00 2001 From: mashizora <30516315+mashizora@users.noreply.github.com> Date: Tue, 13 Oct 2026 15:10:19 +0800 Subject: [PATCH 1/5] fix: validate page sequence numbers before reassembling packets --- src/reading.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/reading.rs b/src/reading.rs index ea8fca5..9388a23 100644 --- a/src/reading.rs +++ b/src/reading.rs @@ -473,6 +473,10 @@ impl BasePacketReader { if pg_prs.bi.first_page { tri!(Err(OggReadError::InvalidData)); } + // Check page sequence numbers before joining packet data. + if pg_prs.bi.sequence_num != inf.bi.sequence_num.wrapping_add(1) { + tri!(Err(OggReadError::InvalidData)); + } if pg_prs.bi.starts_with_continued != inf.bi.ends_with_continued { if !self.has_seeked { tri!(Err(OggReadError::InvalidData)); From d6d533c6c3e8d400771881fdae67b38644b2da6b Mon Sep 17 00:00:00 2001 From: mashizora <30516315+mashizora@users.noreply.github.com> Date: Tue, 13 Oct 2026 15:57:57 +0800 Subject: [PATCH 2/5] fix: report unexpected EOF for incomplete Ogg pages --- src/reading.rs | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/reading.rs b/src/reading.rs index 9388a23..5959063 100644 --- a/src/reading.rs +++ b/src/reading.rs @@ -654,16 +654,13 @@ impl UntilPageHeaderReader { })); if rd_len == 0 { - // Reached EOF. This means we're in one of these cases: - // 1. If we have read nothing yet (self.read_amount == 0), - // there is no data but ogg data, meaning the stream - // ends legally and without corruption. - // 2. If we have read something, there is corruption here. - // The ogg spec doesn't say whether random data past the - // last ogg page is allowed, and ogginfo complains about - // that. But files with trailing garbarge can be played - // back just fine by oggdec, VLC and other players, so - // just ignore that to meet user expectations. + if let FoundWithNeeded(_) = self.mode { + // Found "OggS", but the rest of the header is missing. + return Err(Error::new(ErrorKind::UnexpectedEof, + "Incomplete Ogg page header").into()); + } + + // No page start was found. Ignore any trailing bytes. return Ok(Res::Eof); } self.read_amount += rd_len; @@ -1191,8 +1188,16 @@ pub mod async_api { fn decode_eof(&mut self, buf :&mut BytesMut) -> Result, OggReadError> { - // Ugly hack for "bytes remaining on stream" error - return self.decode(buf); + let page = tri!(self.decode(buf)); + // Allow trailing bytes, but report an unfinished page. + if page.is_none() { + match self.state { + PageDecodeState::Head if !buf.starts_with(b"OggS") => (), + _ => return Err(Error::new(ErrorKind::UnexpectedEof, + "Incomplete Ogg page").into()), + } + } + Ok(page) } } From b849bec7e723f95d412cac20eddb0901c5ff56ce Mon Sep 17 00:00:00 2001 From: mashizora <30516315+mashizora@users.noreply.github.com> Date: Tue, 13 Oct 2026 16:07:26 +0800 Subject: [PATCH 3/5] fix: check OggS capture pattern in async reader --- src/reading.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/reading.rs b/src/reading.rs index 5959063..a081c96 100644 --- a/src/reading.rs +++ b/src/reading.rs @@ -1163,6 +1163,9 @@ pub mod async_api { self.state = match ::std::mem::replace(&mut self.state, InUpdate) { Head => { + if !consumed_buf.starts_with(b"OggS") { + return Err(OggReadError::NoCapturePatternFound); + } let mut hdr_buf = [0; 27]; // TODO once we have const generics, the copy below can be done // much nicer, maybe with a new into_array fn on Vec's From 2e1ab72d9bfba31ce5dc601403c492df2c529aef Mon Sep 17 00:00:00 2001 From: mashizora <30516315+mashizora@users.noreply.github.com> Date: Tue, 13 Oct 2026 16:17:31 +0800 Subject: [PATCH 4/5] fix: reject pages with granule position -1 when packets end --- src/reading.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/reading.rs b/src/reading.rs index a081c96..0f41a49 100644 --- a/src/reading.rs +++ b/src/reading.rs @@ -339,6 +339,10 @@ impl PageParser { tri!(Err(OggReadError::HashMismatch(self.checksum, hash_calculated))); } } + // A granule position of -1 means no packet ends on this page. + if self.bi.absgp == u64::MAX && self.packet_count > 0 { + return Err(OggReadError::InvalidData); + } self.segments_or_packets_buf = packet_data; Ok(OggPage(self)) } From 31c94b762abb12237b32d4d25a077f6bfe99d1fd Mon Sep 17 00:00:00 2001 From: mashizora <30516315+mashizora@users.noreply.github.com> Date: Tue, 13 Oct 2026 16:20:06 +0800 Subject: [PATCH 5/5] fix: reject pages after EOS in the same logical stream --- src/reading.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reading.rs b/src/reading.rs index 0f41a49..bd299bf 100644 --- a/src/reading.rs +++ b/src/reading.rs @@ -474,7 +474,7 @@ impl BasePacketReader { match self.page_infos.entry(pg_prs.stream_serial) { Entry::Occupied(mut o) => { let inf = o.get_mut(); - if pg_prs.bi.first_page { + if pg_prs.bi.first_page || inf.bi.last_page { tri!(Err(OggReadError::InvalidData)); } // Check page sequence numbers before joining packet data.