ReadTGA() sized both the image and its scratch buffer from the header's
width, height and bpp without comparing them against the input stream,
and never checked how much data the bulk reads actually returned. A
22 byte file declaring a 31232x16382 image at 24bpp therefore allocated
1.5 GB twice over, and, because a short read went undetected, LoadFile()
returned true and handed back an image of the declared size that the
file never contained.
Reject dimensions whose claimed image size cannot fit the stream before
allocating anything: uncompressed types must fit exactly, while the RLE
variants are allowed up to 128:1 expansion, since each packet costs
1 + pixelSize input bytes and yields at most 128 * pixelSize output
bytes. Also check LastRead() after the header read and after each of the
three uncompressed bulk reads, so that a truncated file is reported as a
failure instead of a success, as imagpcx.cpp has done since #26624;
DecodeRLE() already validated its own reads.
Fixes#26760.
Closes#26761.
This function allocates the palette r/g/b arrays from the colour count
given in the XPM header but fills them by walking the colour map, so a
malformed file that reuses the same key on more than one colour line
collapses those lines into a single map entry and leaves the tail of the
arrays uninitialised. wxPalette() then copies that uninitialised memory
into the palette later returned by GetPalette(), and in debug builds the
wxASSERT(i == colors_cnt) at the end fires.
Use the actual number of distinct colours in the map as the size for
the arrays and the palette to fix this.
Also add a test loading an XPM triggering this issue.
Closes#26595.
The image palette can have more than the 256 entries a GIF supports, for
example when the image was loaded from an XPM that declares a larger
colour count. wxGIFHandler_GetPalette(), called when saving a GIF,
copied every entry into the caller's fixed wxRGB pal[256] buffer,
overflowing it.
Such an image can't be represented as a GIF, so reject the save with an
error message instead of overflowing the buffer or writing out a silently
truncated palette.
Closes#26532.
We need to use memmove() and not strncpy() in wxXPMDecoder code as
nothing guarantees that the source and destination regions don't overlap
and they did overlap, in fact, for the test case added as part of
46f928d057 (Fix buffer overflow on invalid width in wxXPMDecoder,
2026-05-27).
Also make the test itself more explicit by hard-coding the test XPM
instead of constructing it dynamically.
See #26519.
Fix integer overflow in wxXPMDecoder::ReadData() when computing
width*chars_per_pixel: a header with a (very) large width could result
in wrapping around and result in reasonable but yet invalid value, which
could let the key-reading loop run off the end of the buffer.
Compute the product in 64-bits to avoid the overflow.
Closes#26519.
The replicate-run branch reads the packet's data byte after only
checking that one source byte remains, but it consumes two (the control
byte plus the data byte). A BODY ending in a lone replicate control byte
then reads one byte past the input buffer. Require two bytes left,
matching the literal-run check above.
Closes#26518.
The SEQ chunk of an ANI file gives a 32-bit image index per animation
step. These values were stored into wxANIFrameInfo::m_imageIndex
verbatim, without any check against the number of icon chunks actually
loaded into m_images. wxANIDecoder::ConvertToImage() and
GetTransparentColour() then used the value as an index into m_images
directly, so a malformed ANI file could trigger an out-of-bounds vector
access when the file is displayed.
Reject the file in Load() if any of the indices is negative or points
past the end of m_images, and also reject files that produced no icon
chunks at all so the subsequent m_images[0] reference is safe.
Closes#26492.
Fix iff body truncation over-read in wxIFFDecoder::ReadIFF:
wxIFFDecoder::ReadIFF() sets chunkLen = dataend - dataptr on the
truncated BODY path, which is 8 too large because bodyptr lives 8 bytes
after dataptr. The non-RLE decode loop below then reads up to 8 bytes
past the end of databuf; clamp chunkLen to dataend - bodyptr instead.
Closes#26505.
ReadTGA() in src/common/imagtga.cpp allocates the palette buffer as
paletteLength * palEntrySize bytes (palette indices 0..paletteLength-1)
but the loop that fills it writes each entry at index paletteStart + i.
The paletteStart and paletteLength values come straight from the TGA
header (bytes 3-7 of the colour map specification) and aren't bounded
against each other. For any file with paletteStart > 0, the calls to
Palette_SetRGB()/Palette_SetRGBA() write past the end of the buffer:
e.g. paletteStart=100, paletteLength=10, palettebpp=24 allocates 30
bytes but writes at offsets 100..129. The subsequent
image->SetPalette(wxPalette((int) paletteLength, &palette[0], ...))
also reads from index 0 onward, so the rest of the loader was already
implicitly assuming paletteStart == 0.
Add an explicit early-return wxTGA_INVFORMAT in the colour-mapped
branch when paletteStart is non-zero, which is the assumption the
existing code makes anyway.
Closes#26493.
The minimum code size byte that follows the local colour table in
wxGIFDecoder::LoadGIF() is only checked for <= 0. dgif() sizes
ab_prefix/ab_tail with allocSize = 4096 + 1, so a value of 12
starts ab_free at 4098 and the first alphabet update at
gifdecod.cpp:457 writes one entry past the end of both arrays. The
existing wxASSERT(ab_free < allocSize) already flagged this in debug
builds. The new wxImage::BadGIFLZWMinCodeSize test feeds a 37-byte
2x1 GIF with code size 12 to LoadFile and asserts it is rejected.
Closes#26501.
The quote-stripping loop in wxXPMDecoder::ReadFile() sets p = q + 1
after strncpy(). If the closing " was missing, q stopped at the
buffer terminator, so p ended up one past it and the outer for-loop's
p++ then dereferenced two bytes off the end of the wxCharBuffer.
Mirror the already-existing /*-comment treatment and break out of the
loop when *q == '\0'.
Closes#26499.
wxIFFDecoder::ReadIFF() in src/common/imagiff.cpp parses the BMHD
chunk without bounding the width, height or bitplane count. The
subsequent pixel buffer is allocated with
m_image->p = new byte[bmhd_width * bmhd_height * 3];
using signed-int multiplication. With bmhd_width = 21849 and
bmhd_height = 65535 (both legal 16-bit BMHD values) the product
4,295,622,645 overflows int and wraps down to 655,349, so only
~640 KiB is actually allocated. The BODY decode loop then writes
3 * bmhd_width bytes per row, so a BODY chunk supplying just 10
lineskips of zeros (lineskip = 2732 for this width, total 27,320
bytes) is enough to overrun the allocation. A bmhd_bitplanes or
bmhd_width of zero also makes lineskip * bmhd_bitplanes zero and
causes a divide-by-zero in the height computation a few lines
later.
Reject malformed BMHD chunks at parse time: require positive
width, height and bitplane count, cap the bitplane count at 32
(the largest format the decoder handles is 24-bit ILBM), and cap
bmhd_width * bmhd_height so that the product multiplied by 3
stays within INT_MAX. This makes the existing int-typed buffer
size computation safe and keeps the patch local to the BMHD
parsing branch.
A unit test in tests/image/image.cpp builds the malformed IFF
described above in memory, registers the IFF handler and asserts
that LoadFile() rejects it; without the fix the test triggers a
heap-buffer-overflow during BODY decoding.
Closes#26497.
In src/common/imagbmp.cpp::LoadBMPData() the absolute-mode branches of
both the BI_RLE4 (around line 763) and BI_RLE8 (around line 870) decode
loops increment 'column' for each pixel without checking it against
'width'. The image buffer is sized width * height * 3 and 'poffset' is
computed as line * width * 3 + column * 3, so an absolute escape with
a count larger than (width - column) keeps writing through the rest of
the row and into adjacent rows or past the end of the buffer entirely
on the last decoded scanline.
The neighbouring encoded-mode branches at lines 798 and 896 already use
"&& column < width" to clamp runs to the row width, and the delta-mode
branch at lines 758 and 863 rejects out-of-range row offsets, so the
absolute branches are the only RLE paths left without bounds checking.
Reject the file with return false when the absolute run would extend
past the right edge of the current row, matching the existing
"return false on malformed input" pattern in the same function.
Add a regression test that loads a 4x4 8bpp RLE BMP with an absolute
escape claiming 100 pixels on the first row and expects the loader to
fail rather than write past the image buffer.
Closes#26496.
Following review on the previous commit, switch from silently ignoring
an out-of-bounds BMHD transparent colour index to rejecting the file
outright in wxIFFDecoder::ConvertToImage. The earlier "ignore" approach
let the load succeed, which made the BadIFF test pass only as a side
effect of corruption from the unrelated overrun and so failed
intermittently in some wxMSW builds.
Apply the bounds check using the form suggested in the PR review.
The transparent colour index from the IFF BMHD chunk is a 16-bit
value stored unclamped in m_image->transparent. Using it as a
palette index without checking it against the CMAP-derived colour
count writes 3 bytes at an attacker-controlled offset past the
palette buffer.
Validate the index against the actual palette size before applying
the magenta mask.
When the inner loop scanning for the '*/' that closes a '/*' comment
exits because *q == '\0' (no closing marker before end-of-buffer),
the subsequent strlen(q + 2) reads past the allocated wxCharBuffer.
Bail out of the outer loop in that case.
Closes#26442, #26444.
The PCX header's width and bytesperline are independent fields,
never cross-validated. A header with width > bytesperline produces a
small per-line buffer p and then reads p[i] for i < width past the
buffer end (the 24-bit branch additionally reads p[i + 2 * bytesperline]).
Reject the file when width exceeds bytesperline, or when either is
non-positive.
Closes#26441, #26443.
The non-RLE 8bpp branch at imagbmp.cpp:903, plus the RLE absolute and
RLE encoded branches a few lines above, all index cmap[aByte] without
checking aByte against the palette colour count. A BMP that pairs a
small palette with a colour-index byte >= ncolors reads past the
palette and the value flows into the decoded pixel.
Reject the file (return false) at each site, matching the surrounding
"return false on malformed input" pattern.
Closes#26438.
Closes#26439.
Add wxIMAGE_OPTION_PNG_DESCRIPTION which is filled with the contents of
the (first) "Description" chunk from the PNG file when loading it and
saved into such (iTXt) chunk when saving.
Fixes#25556.
Closes#25565.
In all ports implementing creating wxCursor from wxImage the code for
creating it from wxBitmap was already there, so just factor it out to
allow using it directly, to avoid a conversion to and from wxImage when
we already have a wxBitmap.
For the other ports (wxX11 and wxDFB), leave this ctor unimplemented,
just as it was already the case for the one taking wxImage.
Detected by undefined behavior sanitizer, on a file with 16 bpp and a color
component of more than 8 bits. Fix by using a different, and more accurate,
method to scale color components to 8 bits.
The rules are different than for BMP files. Broken by b21642b72b (Fix loading
BMP files which use 32bpp but do not have valid alpha (#24939), 2024-11-04)
See #24955
Based on the behavior of WIN32 LoadFile(), and the gdk-pixbuf BMP loader source
code, the alpha values are ignored unless the compression method is BI_BITFIELDS.
See #10915, #24219
Add a unit test case checking that we don't crash any longer and still
read the image meta data correctly, even if not the image itself.
Also add another check for EOF and possibly invalid "bits" value in
another place where we were not checking for it when reading from the
stream.
Closes#23409.
Co-authored-by: PB <PBfordev@gmail.com>
Revert the change introduced in b473163da2
and restore the setting of the background of delta-RLE bitmaps to the
first entry in the colour table. This retains earlier wxWidgets
behaviour and matches Windows `LoadImage()` with `LR_CREATEDIBSECTION`.
Closes#23638.
Closes#23657.
This fixes several minor bugs in the loading of `BI_BITFIELDS` bitmaps,
related to handling different header sizes and the colour and alpha
masks.
Closes#23601.
Check that loading BMP files directly using wxDIB and via wxImage
built-in support for them produces the same result (which wasn't the
case for the RLE-compressed bitmaps until the last commit).
Calling Clear() wrongly modified any other wxImage objects reusing the
same data.
Fix this by adding the missing call to AllocExclusive() to this function
and also add a check for the image validity which was missing there as
well ever since this function was added back in fc3762b5fc (add
wxImage::Clear (patch by troelsk); closes#10141, 2009-01-09).
Also add a unit test.
Closes#23553.