PR #25037 opened by StressTestor
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25037
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25037.patch

# Summary of changes

Commits: `avformat/wtvdec: fail on a short read of a legacy attribute GUID`,
`avformat/mpegts: stop the raw PCR readahead on a short read`.

Two demuxers read a fixed number of bytes into a stack buffer with avio_read() 
and use the
buffer whether or not the read completed. On a truncated file the bytes are 
never written:

- wtvdec formats the 16-byte GUID attribute into a metadata string, so ffprobe 
prints the
  uninitialised stack contents as a GUID tag.
- mpegts with `-compute_pcr 1` parses the 12-byte PCR lookahead buffer; garbage 
that parses as
  a PCR becomes `pcr_incr`, which is published as the packet duration and 
accumulated into the
  pts of every later packet.

Both are visible on a build with `-ftrivial-auto-var-init=pattern`:

```
ffprobe -show_format wtv-guid-short-read.wtv
  TAG:encoder=aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaaaaaa        (before)
  (no encoder tag from the truncated entry)              (after)

ffprobe -f mpegtsraw -compute_pcr 1 -show_entries packet=duration -of csv 
mpegts-pcr-short-read.ts
  last packet duration=1716393918470                     (before)
  last packet duration=27000000                          (after; same as the 
untruncated file)
```

The mpegts fix stops the lookahead with `break` rather than failing the packet: 
the packet was
already read successfully, and falling back to the previous increment is the 
existing,
documented behaviour when no next PCR is found. `-compute_pcr 1` output is 
byte-identical
before and after for the lavf-ts FATE file and for the reproducer, both from a 
file and from a
pipe.

The two reproducer files are small and generated by a script; happy to attach 
them or provide
them for fate-suite if wanted.

🤖 Generated with [Claude Code](https://claude.com/claude-code)


From f61e911d27dc294eafdfb745caafa08e57d6b38c Mon Sep 17 00:00:00 2001
From: StressTestor <[email protected]>
Date: Sun, 11 Oct 2026 22:26:31 -0600
Subject: [PATCH 1/2] avformat/wtvdec: fail on a short read of a legacy
 attribute GUID

get_tag() reads a 16-byte GUID attribute into a stack buffer with
avio_read() and ignores the return value. On a legacy_attrib entry
that is cut inside the GUID, the 16 bytes are never written and the
uninitialised stack contents are formatted with PRI_PRETTY_GUID and
published as a metadata string; ffprobe prints them as e.g.
"TAG:encoder=aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaaaaaa" on a build with
-ftrivial-auto-var-init=pattern.

Use ff_get_guid(), which reads with ffio_read_size() and fails on a
short read, and return without adding the tag, as get_tag() already
does when a string attribute is empty.

Same class as c2394a0ff3 (binka), 29f513af78 (fsb), 4b47405e1c (genh).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: StressTestor <[email protected]>
---
 libavformat/wtvdec.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libavformat/wtvdec.c b/libavformat/wtvdec.c
index adea4d9d39..e11b00937d 100644
--- a/libavformat/wtvdec.c
+++ b/libavformat/wtvdec.c
@@ -496,7 +496,8 @@ static void get_tag(AVFormatContext *s, AVIOContext *pb, 
const char *key, int ty
         snprintf(buf, sizeof(buf), "%u", avio_rl16(pb));
     } else if (type == 6 && length == 16) {
         ff_asf_guid guid;
-        avio_read(pb, guid, 16);
+        if (ff_get_guid(pb, &guid) < 0)
+            return;
         snprintf(buf, sizeof(buf), PRI_PRETTY_GUID, ARG_PRETTY_GUID(guid));
     } else if (type == 2 && !strcmp(key, "WM/Picture")) {
         get_attachment(s, pb, length);
-- 
2.52.0


From 1478a420686021b00bdfcdd440df4d2d05833b19 Mon Sep 17 00:00:00 2001
From: StressTestor <[email protected]>
Date: Sun, 11 Oct 2026 22:26:31 -0600
Subject: [PATCH 2/2] avformat/mpegts: stop the raw PCR readahead on a short
 read

mpegts_raw_read_packet() with compute_pcr looks ahead up to
MAX_PACKET_READAHEAD packets for the next PCR, reading 12 bytes of
each candidate into a stack buffer with avio_read() and ignoring the
return value. When the lookahead runs past the end of the file the
buffer keeps whatever it held, uninitialised on the first iteration,
and parse_pcr() is run on it. If those bytes happen to look like a
PCR, the garbage difference becomes pcr_incr and is published as the
duration of this packet and the pts of every later one. On a build
with -ftrivial-auto-var-init=pattern a file ending in a partial
packet yields duration=1716393918470 instead of 27000000.

Stop the readahead when the seek or the read fails. The packet itself
was already read successfully, and the "no next PCR found" fallback
to the previous increment is the designed behaviour for this case, so
failing the packet would be wrong.

Same class as c2394a0ff3 (binka), 29f513af78 (fsb), 4b47405e1c (genh).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: StressTestor <[email protected]>
---
 libavformat/mpegts.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c
index c013452e97..8c7df85173 100644
--- a/libavformat/mpegts.c
+++ b/libavformat/mpegts.c
@@ -3673,8 +3673,9 @@ static int mpegts_raw_read_packet(AVFormatContext *s, 
AVPacket *pkt)
             /* we read the next PCR (XXX: optimize it by using a bigger buffer 
*/
             pos = avio_tell(s->pb);
             for (i = 0; i < MAX_PACKET_READAHEAD; i++) {
-                avio_seek(s->pb, pos + i * ts->raw_packet_size, SEEK_SET);
-                avio_read(s->pb, pcr_buf, 12);
+                if (avio_seek(s->pb, pos + i * ts->raw_packet_size, SEEK_SET) 
< 0 ||
+                    avio_read(s->pb, pcr_buf, sizeof(pcr_buf)) != 
sizeof(pcr_buf))
+                    break;
                 if (parse_pcr(&next_pcr_h, &next_pcr_l, pcr_buf) == 0) {
                     /* XXX: not precise enough */
                     ts->pcr_incr =
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to