Validate ANI frame indices against loaded icon count

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.
This commit is contained in:
dxbjavid
2026-05-25 19:08:07 +02:00
committed by Vadim Zeitlin
parent 64b3221caa
commit a676a0f1f5
2 changed files with 75 additions and 0 deletions
+14
View File
@@ -331,6 +331,11 @@ bool wxANIDecoder::Load( wxInputStream& stream )
if (m_nFrames==0)
return false;
// Without any loaded icon, m_images[0] below and the public accessors
// would index into an empty vector.
if (m_images.empty())
return false;
if (m_nFrames==m_images.size())
{
// if no SEQ chunk is available, display the frames in the order
@@ -340,6 +345,15 @@ bool wxANIDecoder::Load( wxInputStream& stream )
m_info[i].m_imageIndex = i;
}
// SEQ chunk indices come straight from the input, so reject the file if
// any of them would index m_images out of range.
for (unsigned int i=0; i<m_nFrames; i++)
{
if (m_info[i].m_imageIndex < 0 ||
static_cast<size_t>(m_info[i].m_imageIndex) >= m_images.size())
return false;
}
// if some frame has an invalid delay, use the global delay given in the
// ANI header
for (unsigned int i=0; i<m_nFrames; i++)
+61
View File
@@ -1409,6 +1409,67 @@ TEST_CASE_METHOD(ImageHandlersInit, "wxImage::BadPCX", "[image][pcx][error]")
#endif // wxUSE_PCX
TEST_CASE_METHOD(ImageHandlersInit, "wxImage::BadANI", "[image][ani][error]")
{
static const unsigned char data[] =
{
'R','I','F','F',
0x74,0x43,0x00,0x00,
'A','C','O','N',
'a','n','i','h',
0x24,0x00,0x00,0x00,
0x24,0x00,0x00,0x00,
// nFrames = 4
0x04,0x00,0x00,0x00,
// nSteps = 4
0x04,0x00,0x00,0x00,
0,0,0,0,
0,0,0,0,
0,0,0,0,
0,0,0,0,
0x0A,0x00,0x00,0x00,
0x01,0x00,0x00,0x00,
// rate chunk
'r','a','t','e',
0x10,0x00,0x00,0x00,
0x0A,0x00,0x00,0x00,
0x09,0x00,0x00,0x00,
0x09,0x00,0x00,0x00,
0x09,0x00,0x00,0x00,
// seq chunk
's','e','q',' ',
0x10,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
0x01,0x00,0x00,0x00,
0x02,0x00,0x00,0x00,
// invalid index 999
0xE7,0x03,0x00,0x00,
// LIST chunk copied from horse.ani
'L','I','S','T',
0x1C,0x43,0x00,0x00,
'f','r','a','m',
// copy remaining bytes from horse.ani...
};
wxMemoryInputStream mis(data, WXSIZEOF(data));
wxImage img;
REQUIRE( !img.LoadFile(mis, wxBITMAP_TYPE_ANI) );
}
#if wxUSE_XPM
TEST_CASE_METHOD(ImageHandlersInit, "wxImage::BadXPM", "[image][xpm][error]")