Skip to content

Fix out-of-bounds string reads on truncated compressed-RTF in the TNEF decoder - #10269

Merged
alecpl merged 1 commit into
roundcube:masterfrom
MiMoHo:held-tnef-rtf-oob
Jul 18, 2026
Merged

Fix out-of-bounds string reads on truncated compressed-RTF in the TNEF decoder#10269
alecpl merged 1 commit into
roundcube:masterfrom
MiMoHo:held-tnef-rtf-oob

Conversation

@MiMoHo

@MiMoHo MiMoHo commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Problem

rcube_tnef_decoder::_decompressRTF() reads compressed-RTF input byte by byte with ord($data[$in++]). On truncated or otherwise malformed compressed-RTF payloads the read index $in can advance past the end of the input within a single loop iteration, producing "Uninitialized string offset" PHP warnings (and reading out of bounds). The recent compressed-size sanity check (commit ed56e08) did not close this: the only input-exhaustion guard was placed at the bottom of the loop, so multiple ord($data[$in++]) reads in the back-reference branch — and the next iteration's flags read — could still run past strlen($data).

Root cause

program/lib/Roundcube/rcube_tnef_decoder.php, in _decompressRTF(): the if ($in >= $max_len) break; check sat at the end of the while body (previously ~line 551). Within one iteration the code performs up to three separate reads (flags byte, then offset+length in the back-reference branch), and the trailing check does not prevent the next iteration's ord($data[$in++]) flags read when $in has already reached $max_len.

Fix

Move the input-exhaustion check to the top of the loop and add a per-branch guard verifying that the required bytes remain before each read:

  • top of loop: if ($in >= $max_len) break; (covers the flags read)
  • back-reference branch: if ($in + 1 >= $max_len) break; (needs two more bytes)
  • literal branch: if ($in >= $max_len) break; (needs one more byte)

Decompression output for valid input is unchanged.

Testing

Added TnefDecoderTest::test_decompressRTF_truncated, which feeds several truncated compressed-RTF payloads to _decompressRTF() with an error handler that promotes PHP warnings to exceptions. The test fails on current master ("Uninitialized string offset" at the flags read) and passes with the fix. The full tests/Framework/TnefDecoderTest.php suite (5 tests, 51 assertions) passes, and phpstan (level 4) reports no new errors on the changed file.

@MiMoHo
MiMoHo force-pushed the held-tnef-rtf-oob branch from 54d79a8 to 5eae209 Compare July 16, 2026 08:14
@alecpl
alecpl merged commit 62d8cc1 into roundcube:master Jul 18, 2026
17 checks passed
@alecpl alecpl added this to the 1.7.3 milestone Jul 18, 2026
alecpl pushed a commit that referenced this pull request Jul 18, 2026
alecpl pushed a commit that referenced this pull request Jul 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants