Skip to content

[FIX] WTV: bound the metadata lengths read_header() takes from the file - #2435

Open
pranayr710 wants to merge 1 commit into
CCExtractor:masterfrom
pranayr710:fix-wtv-read-header
Open

pranayr710 wants to merge 1 commit into
CCExtractor:masterfrom
pranayr710:fix-wtv-read-header

Conversation

@pranayr710

Copy link
Copy Markdown
Contributor

Fixes #2433.

read_header() parses the root directory of a .wtv into a 1024-byte
parsebuf and takes two lengths straight out of the file without tying either
to that buffer.

len - 32 underflowed the size_t buffered_read() takes. len (16 bit)
counts the 32-byte record as well, but only the upper bound was checked, so a
file declaring len < 32 turned the count into a value near 2^64.
wtv_len_plausible() already rejects raw_len < 32 for the same field in the
outer element loop, so this patch applies the same rule here.

text_len was unbounded. It is a 32-bit character count for the UTF-16 name
that starts 8 bytes into the body, so it has to satisfy
8 + 2 * text_len <= len - 32. Nothing checked it, so the conversion loop walks
off parsebuf; and because text_len + 1 is computed in uint32_t,
text_len == UINT32_MAX gives malloc(0) while string[text_len] = '\0' still
writes roughly 4 GB past it. The .entries. branch then reads eight bytes
further still.

Also frees parsebuf on the "Missing WTV header" path, which was the only error
return in the function that leaked it.

Verification

Built from master (d7eb132) with -fsanitize=address,undefined, against six
crafted .wtv files (.wtv is recognised from its first four bytes alone, so
each is about 4 KB). Generator is in the issue.

file before after
wtv_textlen (text_len = 5000) heap-buffer-overflow in read_header rejected: Metadata string longer than its entry!
wtv_textmax (text_len = UINT32_MAX) SEGV in read_header, 3 of 3 runs rejected: Metadata string longer than its entry!
wtv_ok (self-consistent) Premature end of file!, exit 10 identical
wtv_trunc (header only) Premature end of file!, exit 10 identical
wtv_len0 (len = 0) Premature end of file!, exit 0 rejected: Metadata entry shorter than its own header!
wtv_len16 (len = 16) Premature end of file!, exit 0 rejected: Metadata entry shorter than its own header!

To be straight about the last two: the huge size_t makes buffered_read()
return short, so the existing "Premature end of file" check catches it and I
could not turn the underflow into an observable overflow. That guard is
defensive — it stops a bogus count reaching buffered_read() at all, rather
than fixing a demonstrated crash. The two text_len cases are real crashes.

No new compiler warnings (the has_teletext -Wunused-but-set-variable in this
file is pre-existing).

(Prepared with AI assistance; the reproducers and the sanitizer runs are my
own.)

read_header() walks the root directory of a .wtv into a 1024-byte parsebuf
and trusts two length fields out of the file:

  * len (uint16) counts the 32-byte record as well, but only the upper
    bound was checked, so len < 32 made len - 32 wrap round in the size_t
    that buffered_read() takes for its count;

  * text_len (uint32) is a character count for the UTF-16 name that starts
    8 bytes into the body. Nothing tied it to the body that was actually
    read, so the conversion loop walks off parsebuf, and at UINT32_MAX
    malloc(text_len + 1) wraps to malloc(0) while string[text_len] still
    writes ~4 GB past it. The .entries. branch then reads 8 bytes further
    still.

Reject len < 32 the way wtv_len_plausible() already does for the same
field, keep the body length around, and require the string and the value
that follows it to fit in it. Also free parsebuf on the "Missing WTV
header" path, which was the one error return in this function that leaked
it.

A 4 KB crafted file (.wtv is recognised from its first four bytes alone)
segfaults in read_header on an ASan build before this change and is
rejected cleanly after it.

Fixes CCExtractor#2433
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] WTV read_header(): unchecked len and text_len from the file crash the parser (SEGV, 4 KB reproducer)

2 participants