Skip to content

fix(dvb): bound language count and correct descriptor stride in parse_dvb_description - #2418

Open
pantha704 wants to merge 1 commit into
CCExtractor:masterfrom
pantha704:fix/dvb-desc-descriptor-bounds
Open

pantha704 wants to merge 1 commit into
CCExtractor:masterfrom
pantha704:fix/dvb-desc-descriptor-bounds

Conversation

@pantha704

Copy link
Copy Markdown
Contributor

What

Two defects in parse_dvb_description() (src/lib_ccx/dvb_subtitle_decoder.c), both reported in #2417 by @Haind03 with a full technical write-up: https://hackmd.io/@haindpwn/ccextractor-parse-dvb-description-stack-oob-write

1. Unbounded language count → stack out-of-bounds write

cfg->n_language = len / 8 was taken straight from the descriptor length byte, so with a length byte of 56 or more n_language reached 7..31, while lang_index, sub_type, composition_id and ancillary_id are each MAX_LANGUAGE_PER_DESC (5) elements. The existing check only printed a warning. The struct is a stack local in parse_PMT(), so this wrote past the 52-byte struct dvb_config into the caller's frame.

Fix: clamp n_language to MAX_LANGUAGE_PER_DESC after the warning.

2. Quadratic read stride → out-of-bounds read

for (int i = 0; i < cfg->n_language; i++, data_ptr += i * 8)

The comma operator evaluates i++ first, so the cursor advanced by 4*i*(i+1) bytes instead of 8 per entry, reading past the descriptor buffer from the ninth entry on.

Fix: data_ptr += 8.

Verification

Built this translation unit with -fsanitize=address and drove parse_dvb_description() directly with a 248-byte descriptor (n_language = 31).

Before:

ERROR: AddressSanitizer: stack-buffer-overflow
WRITE of size 2 at 0x... in parse_dvb_description src/lib_ccx/dvb_subtitle_decoder.c:2047
  This frame has 1 object(s):
    [32, 84) 'cfg' (line 15) <== Memory access at offset 84 overflows this variable

After:

ret=0 n_language=5 (MAX=5)

n_language is clamped to 5, all four arrays stay in bounds, and reads stay inside the descriptor. The matroska.c caller passes len = 8, which is unaffected.

I could not run the repo's tests/ harness here because it links the full object set and this machine lacks the OCR dependencies (tesseract, leptonica); the check above is a targeted AddressSanitizer run against the affected function. A regression transport stream with descriptor length bytes 56 and 248 would be worth adding to tests/ separately.

Credit

Both defects and the suggested fix come from @Haind03's write-up in #2417. This PR just applies the two changes and confirms them under ASan.

parse_dvb_description() derived the language count from the descriptor
length byte without limiting it to the size of the dvb_config arrays, and
advanced the read cursor by a growing stride instead of a fixed entry
width.

- Clamp n_language to MAX_LANGUAGE_PER_DESC after the existing warning.
  Previously a descriptor longer than 40 bytes made the loop write past
  lang_index/sub_type/composition_id/ancillary_id, which are each five
  elements, corrupting the caller's stack frame in parse_PMT().
- Change the loop increment from data_ptr += i * 8 to data_ptr += 8.
  Because the comma operator evaluates i++ before the increment, the old
  form advanced the cursor quadratically (4*i*(i+1) bytes) and read past
  the descriptor buffer from the ninth entry onwards.

Verified with an AddressSanitizer build of this translation unit: a
248-byte descriptor (n_language = 31) produced a stack-buffer-overflow
write at the ancillary_id[6] store before the change and no diagnostics
after it, with n_language clamped to 5.
@ccextractor-bot

Copy link
Copy Markdown
Collaborator
CCExtractor CI platform finished running the test files on linux. 77/237 tests matched the approved output:
Report Name Tests Passed
Broken 0/13
CEA-708 1/14
DVB 0/7
DVD 0/3
DVR-MS 0/2
General 0/27
Hardsubx 1/1
Hauppage 0/3
MP4 0/3
NoCC 10/10
Options 65/86
Teletext 0/21
WTV 0/13
XDS 0/34

160 tests do not match the approved output. That is the pass/fail verdict. Whether this branch caused it is a separate question, answered below.


Compared with the tip of master — test 9744, commit 0bf2a94:

  • 31 pass there and fail here
  • 0 fail there and pass here
  • 7 fail on both, with different output
  • 122 fail on both, byte for byte the same

Pass there, fail here:

  • ccextractor --autoprogram --out=ttxt --latin1 --ucla --xds 725a49f871...
  • ccextractor --autoprogram --out=ttxt --xds --latin1 --ucla d037c7509e...
  • ccextractor --autoprogram --out=smptett --latin1 --ucla e274a73653...
  • ccextractor --autoprogram --out=ttxt --xds --latin1 --ucla e274a73653...
  • ccextractor --autoprogram --out=ttxt --xds --latin1 --ucla 85058ad37e...
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla --xds b22260d065...
  • ccextractor --autoprogram --out=srt --latin1 --ucla b22260d065...
  • ccextractor --autoprogram --out=ttxt --latin1 --xds --ucla c813e713a0...
  • ccextractor --autoprogram --out=srt --latin1 --ucla c813e713a0...
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla --xds 27fab4dbb6...
  • ccextractor --autoprogram --out=srt --latin1 --ucla 27fab4dbb6...
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla --xds bbd5bb52fc...
  • ccextractor --autoprogram --out=srt --latin1 --ucla bbd5bb52fc...
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla --xds b992e0cccb...
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla --xds d0291cdcf6...
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla 7d2730d38e...
  • ccextractor --autoprogram --out=srt --latin1 --ucla 7d2730d38e...
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla --xds c8dc039a88...
  • ccextractor --autoprogram --out=srt --latin1 --ucla c8dc039a88...
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla --xds 53339f3455...
  • ccextractor --autoprogram --out=srt --latin1 --ucla 53339f3455...
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla --xds 83b03036a2...
  • ccextractor --autoprogram --out=srt --latin1 --ucla 83b03036a2...
  • ccextractor --autoprogram --out=ttxt --latin1 5ae2007a79...
  • ccextractor --autoprogram --out=ttxt --latin1 1e44efd810...
  • ccextractor --autoprogram --out=ttxt --latin1 add511677c...
  • ccextractor --service 1 --out=ttxt da904de35d...
  • ccextractor --out=txt --ucla c83f765c66...
  • ccextractor --endcreditstext "CCextractor Ends crdit Testing" addf5e2fc9...
  • ccextractor --endcreditsforatleast 3 --endcreditstext "CCextractor Ends crdit Testing" addf5e2fc9...
  • ccextractor --endcreditsforatmost 2 --endcreditstext "CCextractor Ends crdit Testing" addf5e2fc9...

Fail on both but produce different output — behaviour moved even though the verdict did not:

  • ccextractor --autoprogram --out=srt --latin1 --ucla d037c7509e...
  • ccextractor --startcreditsnotafter 2 --startcreditstext "CCextractor Start crdit Testing" c4dd893cb9...
  • ccextractor --startcreditsforatleast 1 --startcreditstext "CCextractor Start crdit Testing" c4dd893cb9...
  • ccextractor --startcreditsforatmost 2 --startcreditstext "CCextractor Start crdit Testing" c4dd893cb9...
  • ccextractor --tpage 801 4e56e88ba4...
  • ccextractor --tverbose 4e56e88ba4...
  • ccextractor --teletext 4e56e88ba4...

Compared with the commit this branch was cut from — test 9750, commit d7eb132:

  • 49 pass there and fail here
  • 0 fail there and pass here
  • 9 fail on both, with different output
  • 102 fail on both, byte for byte the same

Pass there, fail here:

  • ccextractor --out=srt --latin1 97cc394d87...
  • ccextractor --out=srt --latin1 10f0f77cf4...
  • ccextractor --out=srt --latin1 df3b4d62d3...
  • ccextractor --out=srt --latin1 d7e7dbdf68...
  • ccextractor --out=srt --latin1 76734ac4a7...
  • ccextractor --out=srt --latin1 c791382c94...
  • ccextractor --out=srt --latin1 f673b2f916...
  • ccextractor --out=srt --latin1 da75bdee47...
  • ccextractor --out=srt --latin1 bd6f33a669...
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla --xds 725a49f871...
  • ccextractor --autoprogram --out=ttxt --xds --latin1 --ucla d037c7509e...
  • ccextractor --autoprogram --out=smptett --latin1 --ucla e274a73653...
  • ccextractor --autoprogram --out=ttxt --xds --latin1 --ucla e274a73653...
  • ccextractor --autoprogram --out=ttxt --xds --latin1 --ucla 85058ad37e...
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla --xds b22260d065...
  • ccextractor --autoprogram --out=srt --latin1 --ucla b22260d065...
  • ccextractor --autoprogram --out=ttxt --latin1 --xds --ucla c813e713a0...
  • ccextractor --autoprogram --out=srt --latin1 --ucla c813e713a0...
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla --xds 27fab4dbb6...
  • ccextractor --autoprogram --out=srt --latin1 --ucla 27fab4dbb6...
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla --xds bbd5bb52fc...
  • ccextractor --autoprogram --out=srt --latin1 --ucla bbd5bb52fc...
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla --xds b992e0cccb...
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla --xds d0291cdcf6...
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla 7d2730d38e...
  • ccextractor --autoprogram --out=srt --latin1 --ucla 7d2730d38e...
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla --xds c8dc039a88...
  • ccextractor --autoprogram --out=srt --latin1 --ucla c8dc039a88...
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla --xds 53339f3455...
  • ccextractor --autoprogram --out=srt --latin1 --ucla 53339f3455...
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla --xds 83b03036a2...
  • ccextractor --autoprogram --out=srt --latin1 --ucla 83b03036a2...
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla --xds 7d3f25c32c...
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla --xds f41d4c29a1...
  • ccextractor --autoprogram --out=srt --latin1 --ucla f41d4c29a1...
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla --xds 88cd42b89a...
  • ccextractor --autoprogram --out=srt --latin1 --ucla 88cd42b89a...
  • ccextractor --autoprogram --out=srt --latin1 --output-field 2 --ucla 88cd42b89a...
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla --xds 7f41299cc7...
  • ccextractor --autoprogram --out=srt --latin1 --ucla 7f41299cc7...
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla --xds 0069dffd21...
  • ccextractor --autoprogram --out=ttxt --latin1 5ae2007a79...
  • ccextractor --autoprogram --out=ttxt --latin1 1e44efd810...
  • ccextractor --autoprogram --out=ttxt --latin1 add511677c...
  • ccextractor --service 1 --out=ttxt da904de35d...
  • ccextractor --out=txt --ucla c83f765c66...
  • ccextractor --endcreditstext "CCextractor Ends crdit Testing" addf5e2fc9...
  • ccextractor --endcreditsforatleast 3 --endcreditstext "CCextractor Ends crdit Testing" addf5e2fc9...
  • ccextractor --endcreditsforatmost 2 --endcreditstext "CCextractor Ends crdit Testing" addf5e2fc9...

Fail on both but produce different output — behaviour moved even though the verdict did not:

  • ccextractor --out=srt --latin1 f23a544ba8...
  • ccextractor --autoprogram --out=srt --latin1 --ucla d037c7509e...
  • ccextractor --autoprogram --out=srt --latin1 --ucla 7d3f25c32c...
  • ccextractor --startcreditsnotafter 2 --startcreditstext "CCextractor Start crdit Testing" c4dd893cb9...
  • ccextractor --startcreditsforatleast 1 --startcreditstext "CCextractor Start crdit Testing" c4dd893cb9...
  • ccextractor --startcreditsforatmost 2 --startcreditstext "CCextractor Start crdit Testing" c4dd893cb9...
  • ccextractor --tpage 801 4e56e88ba4...
  • ccextractor --tverbose 4e56e88ba4...
  • ccextractor --teletext 4e56e88ba4...

This branch changes the behaviour of 38 test(s) relative to the tip of master. Those are the ones worth looking at; anything else in the list fails the same way on both sides.

@ccextractor-bot

Copy link
Copy Markdown
Collaborator
CCExtractor CI platform finished running the test files on windows. 171/237 tests matched the approved output:
Report Name Tests Passed
Broken 9/13
CEA-708 2/14
DVB 0/7
DVD 3/3
DVR-MS 2/2
General 25/27
Hardsubx 1/1
Hauppage 3/3
MP4 3/3
NoCC 10/10
Options 69/86
Teletext 0/21
WTV 12/13
XDS 32/34

66 tests do not match the approved output. That is the pass/fail verdict. Whether this branch caused it is a separate question, answered below.


Compared with the tip of master — test 9693, commit af4b28d:

  • 0 pass there and fail here
  • 157 fail there and pass here
  • 42 fail on both, with different output
  • 24 fail on both, byte for byte the same

Fail on both but produce different output — behaviour moved even though the verdict did not:

Fail there, pass here:


Compared with the commit this branch was cut from: the same run as the tip of master (test 9693), so the comparison above already covers it.


This branch changes the behaviour of 42 test(s) relative to the tip of master. Those are the ones worth looking at; anything else in the list fails the same way on both sides.

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.

2 participants