Repository navigation
[FIX] ISDB: keep the decoder inside the buffer it was handed, and reserve room for get_text()'s terminator - #2436
Open
pranayr710 wants to merge 2 commits into
Open
pranayr710 wants to merge 2 commits into
pranayr710 wants to merge 2 commits into
Conversation
isdbsub_decode() takes a buf_size and never looked at it. Every length
below it comes straight out of the stream and was used unchecked:
* the PES header length nibble could point past the payload;
* group_size is 16 bits, so a data group could claim 64 KB of a
25 byte payload;
* the data unit length is 24 bits, so parse_statement() could be
asked to walk 16 MB;
* nb_lang is a byte, so caption management data could read 255
five byte language entries out of nothing.
parse_statement() also passed the whole statement size to
parse_command() rather than what was left from the current position,
and left ret unassigned on two of its cases -- indeterminate on the
first pass, and a carried-over zero would spin the loop for ever.
Carry the real length down the chain and clamp each stream-supplied
length to it. Truncating rather than erroring keeps the existing
"decode what arrived" behaviour for data that legitimately spans
packets.
Three crafted .ts files (ISDB is selected by a data_component_descriptor
with component_id 0x0008 in the PMT) make an ASan build report a
heap-buffer-overflow in parse_statement or die outright; after this they
are parsed and discarded.
Fixes CCExtractor#2434
reserve_buf(text, len) guarantees capacity for text->used + len, so reserve_buf(sb_text, text->used) on a freshly allocated node reserves exactly text->used bytes -- and the next line writes sb_text->buf[text->used] = '\0', one byte past the end. The allocation rounds up to a multiple of 128, so the write stays inside the block unless text->used is itself an exact multiple of 128. A crafted .ts that lands on that boundary makes an ASan build report a heap-buffer-overflow in get_text. Both copies in get_text() ask for the terminator as well.
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 #2434.
Two independent out-of-bounds accesses in the ISDB caption decoder. They share
a reproducer corpus, so they are in one PR as two commits; the second is a
two-line change and is easy to review on its own.
1. The decoder never looked at the length it was handed
isdbsub_decode()takes abuf_sizeand does not use it, and every lengthbelow it comes straight out of the stream:
group_sizeis 16 bits, so a data group could claim 64 KB of a 25-bytepayload;
parse_statement()could be asked towalk 16 MB;
nb_langis one byte, so caption management data could read 255 five-bytelanguage entries out of nothing.
parse_statement()also passed the whole statement size toparse_command()rather than what was left from the current position, and left
retunassignedon two of its cases — indeterminate on the first pass, and a carried-over
0would spin
buf += retforever.This commit carries the real length down the chain and clamps each
stream-supplied length to what remains. Truncating rather than erroring keeps
the existing "decode what arrived" behaviour for data that legitimately spans
packets.
2.
get_text()had no room for the terminator it writesreserve_buf(text, len)guarantees capacity fortext->used + len, i.e.lenbytes on top of what is used. Both call sites in
get_text()doThe allocation rounds up to a multiple of 128, which hides this until
text->usedis itself an exact multiple of 128.Verification
ISDB is selected by a
data_component_descriptor(tag0xFD,component_id0x0008) on a stream type0x06PID, so a ~1 KB transport stream reaches thedecoder. Generator and a worked example are in the issue. Eight files, built
from master (d7eb132).
Sanitizer build (
-fsanitize=address,undefined), two runs each:isdb_len24(24-bit unit length0xFFFFFF)heap-buffer-overflow in parse_statement, 2/2isdb_len256(text length lands on 256)heap-buffer-overflow in get_text, 2/2isdb_ok(well formed)isdb_hdrskipPlain build, pristine master vs this PR, three runs each, all eight files:
identical exit codes throughout, and
isdb_okproduces a byte-identical SRT(md5
5d6c804a) on both. Same compiler warning count (20), none in this file.Two things worth being straight about:
ptr->buffer, a 2 MBBUFSIZEheap allocation, so the overreads stay insidethe block on an ordinary build and quietly fold stale bytes from earlier
packets into the subtitle text. The sanitizer is what calls it.
AddressSanitizer:DEADLYSIGNALloops onfour of the files. Those appear on both builds at random, including on
files this patch rejects in its first three lines, and never on a build
without sanitizers. I treated them as a build artifact rather than evidence
either way; the two findings above are the deterministic ones.
(Prepared with AI assistance; the reproducers and the sanitizer runs are my
own.)