Repository navigation
[FIX] WTV: bound the metadata lengths read_header() takes from the file - #2435
Open
pranayr710 wants to merge 1 commit into
Open
pranayr710 wants to merge 1 commit into
pranayr710 wants to merge 1 commit into
Conversation
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
5 of 7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #2433.
read_header()parses the root directory of a.wtvinto a 1024-byteparsebufand takes two lengths straight out of the file without tying eitherto that buffer.
len - 32underflowed thesize_tbuffered_read()takes.len(16 bit)counts the 32-byte record as well, but only the upper bound was checked, so a
file declaring
len < 32turned the count into a value near 2^64.wtv_len_plausible()already rejectsraw_len < 32for the same field in theouter element loop, so this patch applies the same rule here.
text_lenwas unbounded. It is a 32-bit character count for the UTF-16 namethat starts 8 bytes into the body, so it has to satisfy
8 + 2 * text_len <= len - 32. Nothing checked it, so the conversion loop walksoff
parsebuf; and becausetext_len + 1is computed inuint32_t,text_len == UINT32_MAXgivesmalloc(0)whilestring[text_len] = '\0'stillwrites roughly 4 GB past it. The
.entries.branch then reads eight bytesfurther still.
Also frees
parsebufon the "Missing WTV header" path, which was the only errorreturn in the function that leaked it.
Verification
Built from master (d7eb132) with
-fsanitize=address,undefined, against sixcrafted
.wtvfiles (.wtvis recognised from its first four bytes alone, soeach is about 4 KB). Generator is in the issue.
wtv_textlen(text_len= 5000)heap-buffer-overflow in read_headerwtv_textmax(text_len=UINT32_MAX)read_header, 3 of 3 runswtv_ok(self-consistent)wtv_trunc(header only)wtv_len0(len= 0)wtv_len16(len= 16)To be straight about the last two: the huge
size_tmakesbuffered_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, ratherthan fixing a demonstrated crash. The two
text_lencases are real crashes.No new compiler warnings (the
has_teletext-Wunused-but-set-variablein thisfile is pre-existing).
(Prepared with AI assistance; the reproducers and the sanitizer runs are my
own.)