Skip to content

Fix heap overflow in the BMP RLE8 decoder (CVE-2020-21427) - #101

Open
jojastahl wants to merge 1 commit into
agruzdev:masterfrom
demig-de:pr/bmp-rle8-fix
Open

jojastahl wants to merge 1 commit into
agruzdev:masterfrom
demig-de:pr/bmp-rle8-fix

Conversation

@jojastahl

Copy link
Copy Markdown

Fixes the heap overflow in the BMP RLE8 decoder that is already public as CVE-2020-21427.

Problem

In LoadPixelDataRLE8, the run length is used as a read size without checking it against the space left in the scanline. When bits has advanced past width (an over-long run, or an RLE_DELTA that moves right), width - bits is negative and is passed to read_proc as a huge unsigned size, writing outside the scanline buffer. The same happens in the absolute mode path.

Change

  • Encoded mode: the run is clamped to width - bits (never below 0).
  • Absolute mode: only the bytes that fit are read into the scanline; the remaining bytes of the run are skipped, the pad byte logic is unchanged.
  • RLE_DELTA: bits and scanline are clamped to the image bounds.
  • Truncated data still ends decoding with a null bitmap as before.

Tests

New TestAPI/testBMP.cpp (testBMPRLE8), built from in-memory 8-bit RLE8 BMPs:

  • a valid image using encoded and absolute runs, with pixel values checked;
  • runs longer than the row and deltas outside the image, which are clamped and decoded without error;
  • a truncated absolute run, which must be rejected (null bitmap).

No crafted exploit input is included; the malformed cases are ordinary truncated or over-long streams.

The test is gated by TEST_WITH_BMP (on by default; compiled out when the BMP plugin is disabled), matching the PCX decoder test.

LoadPixelDataRLE8() let the position in the scanline grow beyond the
image width: through the RLE_DELTA command, and by adding the full length
of an absolute run even if only a part of it fits. After that,
"width - bits" is negative, and the absolute mode passed it as the size
to read_proc, which is converted to a huge unsigned value. The rest of
the file is then written behind the scanline.

- Keep the horizontal position inside [0, width] and the scanline inside
  [0, height] when applying RLE_DELTA, and when advancing after an
  absolute run.
- Read into the scanline only the part of an absolute run which fits
  and skip the rest of the run. The same clamping is applied to encoded
  runs, which were already safe.
- Do not call read_proc with a zero size.

Add a TestAPI test that decodes valid RLE8 data, runs which are longer
than the scanline, deltas outside of the image, and checks that
truncated data is rejected. The test images are built in the test code.
The test is built only if the BMP plugin is built in (see TEST_WITH_BMP).
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.

1 participant