X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/6363699a2849533e011a1757b46b33ecf1a54e57..2334fef6eaf763bc0b9132a86cc57c395c85c557:/src/common/gifdecod.cpp?ds=inline diff --git a/src/common/gifdecod.cpp b/src/common/gifdecod.cpp index f287947799..c9cfe9b16e 100644 --- a/src/common/gifdecod.cpp +++ b/src/common/gifdecod.cpp @@ -8,7 +8,7 @@ // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// -#ifdef __GNUG__ +#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) #pragma implementation "gifdecod.h" #endif @@ -21,6 +21,7 @@ #ifndef WX_PRECOMP # include "wx/defs.h" +# include "wx/palette.h" #endif #if wxUSE_STREAMS && wxUSE_GIF @@ -30,6 +31,24 @@ #include "wx/gifdecod.h" +//--------------------------------------------------------------------------- +// GIFImage constructor +//--------------------------------------------------------------------------- +GIFImage::GIFImage() +{ + w = 0; + h = 0; + left = 0; + top = 0; + transparent = 0; + disposal = 0; + delay = -1; + p = (unsigned char *) NULL; + pal = (unsigned char *) NULL; + next = (GIFImage *) NULL; + prev = (GIFImage *) NULL; +} + //--------------------------------------------------------------------------- // wxGIFDecoder constructor and destructor //--------------------------------------------------------------------------- @@ -97,7 +116,7 @@ bool wxGIFDecoder::ConvertToImage(wxImage *image) const image->Create(GetWidth(), GetHeight()); if (!image->Ok()) - return FALSE; + return false; pal = GetPalette(); src = GetData(); @@ -124,7 +143,7 @@ bool wxGIFDecoder::ConvertToImage(wxImage *image) const image->SetMaskColour(255, 0, 255); } else - image->SetMask(FALSE); + image->SetMask(false); #if wxUSE_PALETTE if (pal) @@ -152,7 +171,7 @@ bool wxGIFDecoder::ConvertToImage(wxImage *image) const *(dst++) = pal[3 * (*src) + 2]; } - return TRUE; + return true; } @@ -189,27 +208,27 @@ bool wxGIFDecoder::IsAnimation() const { return (m_nimages > 1); } bool wxGIFDecoder::GoFirstFrame() { if (!IsAnimation()) - return FALSE; + return false; m_image = 1; m_pimage = m_pfirst; - return TRUE; + return true; } bool wxGIFDecoder::GoLastFrame() { if (!IsAnimation()) - return FALSE; + return false; m_image = m_nimages; m_pimage = m_plast; - return TRUE; + return true; } bool wxGIFDecoder::GoNextFrame(bool cyclic) { if (!IsAnimation()) - return FALSE; + return false; if ((m_image < m_nimages) || (cyclic)) { @@ -222,16 +241,16 @@ bool wxGIFDecoder::GoNextFrame(bool cyclic) m_pimage = m_pfirst; } - return TRUE; + return true; } else - return FALSE; + return false; } bool wxGIFDecoder::GoPrevFrame(bool cyclic) { if (!IsAnimation()) - return FALSE; + return false; if ((m_image > 1) || (cyclic)) { @@ -244,10 +263,10 @@ bool wxGIFDecoder::GoPrevFrame(bool cyclic) m_pimage = m_plast; } - return TRUE; + return true; } else - return FALSE; + return false; } bool wxGIFDecoder::GoFrame(int which) @@ -255,19 +274,19 @@ bool wxGIFDecoder::GoFrame(int which) int i; if (!IsAnimation()) - return FALSE; + return false; if ((which >= 1) && (which <= m_nimages)) { m_pimage = m_pfirst; - for (i = 1; i < which; i++) + for (i = 0; i < which; i++) m_pimage = m_pimage->next; - return TRUE; + return true; } else - return FALSE; + return false; } @@ -309,6 +328,11 @@ int wxGIFDecoder::getcode(int bits, int ab_fin) /* prefetch data */ m_f->Read((void *) m_buffer, m_restbyte); + if (m_f->LastRead() != m_restbyte) + { + code = ab_fin; + return code; + } m_bufp = m_buffer; } @@ -332,12 +356,32 @@ int wxGIFDecoder::getcode(int bits, int ab_fin) // dgif: // GIF decoding function. The initial code size (aka root size) // is 'bits'. Supports interlaced images (interl == 1). -// +// Returns wxGIF_OK (== 0) on success, or an error code if something +// fails (see header file for details) int wxGIFDecoder::dgif(GIFImage *img, int interl, int bits) { - int *ab_prefix = new int[4096]; /* alphabet (prefixes) */ - int *ab_tail = new int[4096]; /* alphabet (tails) */ - int *stack = new int[4096]; /* decompression stack */ + static const int allocSize = 4096 + 1; + int *ab_prefix = new int[allocSize]; /* alphabet (prefixes) */ + if (ab_prefix == NULL) + { + return wxGIF_MEMERR; + } + + int *ab_tail = new int[allocSize]; /* alphabet (tails) */ + if (ab_tail == NULL) + { + delete[] ab_prefix; + return wxGIF_MEMERR; + } + + int *stack = new int[allocSize]; /* decompression stack */ + if (stack == NULL) + { + delete[] ab_prefix; + delete[] ab_tail; + return wxGIF_MEMERR; + } + int ab_clr; /* clear code */ int ab_fin; /* end of info code */ int ab_bits; /* actual symbol width, in bits */ @@ -401,7 +445,27 @@ int wxGIFDecoder::dgif(GIFImage *img, int interl, int bits) { stack[pos++] = ab_tail[code]; code = ab_prefix[code]; + + // Don't overflow. This shouldn't happen with normal + // GIF files, the allocSize of 4096+1 is enough. This + // will only happen with badly formed GIFs. + if (pos >= allocSize) + { + delete[] ab_prefix; + delete[] ab_tail; + delete[] stack; + return wxGIF_INVFORMAT; + } + } + + if (pos >= allocSize) + { + delete[] ab_prefix; + delete[] ab_tail; + delete[] stack; + return wxGIF_INVFORMAT; } + stack[pos] = code; /* push last code into the stack */ abcabca = code; /* save for special case */ @@ -419,10 +483,11 @@ int wxGIFDecoder::dgif(GIFImage *img, int interl, int bits) } } - /* dump stack data to the buffer */ + /* dump stack data to the image buffer */ while (pos >= 0) { - (img->p)[x + (y * (img->w))] = (char)stack[pos--]; + (img->p)[x + (y * (img->w))] = (char) stack[pos]; + pos--; if (++x >= (img->w)) { @@ -438,13 +503,50 @@ int wxGIFDecoder::dgif(GIFImage *img, int interl, int bits) case 3: y += 4; break; case 4: y += 2; break; } - if (y >= (img->h)) + + /* loop until a valid y coordinate has been + found, Or if the maximum number of passes has + been reached, exit the loop, and stop image + decoding (At this point the image is succesfully + decoded). + If we don't loop, but merely set y to some other + value, that new value might still be invalid depending + on the height of the image. This would cause out of + bounds writing. + */ + while (y >= (img->h)) { switch (++pass) { case 2: y = 4; break; case 3: y = 2; break; case 4: y = 1; break; + + default: + /* + It's possible we arrive here. For example this + happens when the image is interlaced, and the + height is 1. Looking at the above cases, the + lowest possible y is 1. While the only valid + one would be 0 for an image of height 1. So + 'eventually' the loop will arrive here. + This case makes sure this while loop is + exited, as well as the 2 other ones. + */ + + // Set y to a valid coordinate so the local + // while loop will be exited. (y = 0 always + // is >= img->h since if img->h == 0 the + // image is never decoded) + y = 0; + + // This will exit the other outer while loop + pos = -1; + + // This will halt image decoding. + code = ab_fin; + + break; } } } @@ -505,21 +607,23 @@ as an End of Information itself) delete [] ab_tail ; delete [] stack ; - return 0; + return wxGIF_OK; } // CanRead: -// Returns TRUE if the file looks like a valid GIF, FALSE otherwise. +// Returns true if the file looks like a valid GIF, false otherwise. // bool wxGIFDecoder::CanRead() { unsigned char buf[3]; - m_f->Read(buf, 3); - m_f->SeekI(-3, wxFromCurrent); + if ( !m_f->Read(buf, WXSIZEOF(buf)) ) + return false; + + m_f->SeekI(-(wxFileOffset)WXSIZEOF(buf), wxFromCurrent); - return (memcmp(buf, "GIF", 3) == 0); + return memcmp(buf, "GIF", WXSIZEOF(buf)) == 0; } @@ -534,27 +638,42 @@ bool wxGIFDecoder::CanRead() // int wxGIFDecoder::ReadGIF() { - int ncolors, bits, interl, transparent, disposal, i; + unsigned int ncolors; + int bits, interl, transparent, disposal, i; long size; long delay; unsigned char type = 0; unsigned char pal[768]; unsigned char buf[16]; GIFImage **ppimg; - GIFImage *pimg, *pprev; + GIFImage *pimg, *pprev; /* check GIF signature */ if (!CanRead()) return wxGIF_INVFORMAT; /* check for animated GIF support (ver. >= 89a) */ - m_f->Read(buf, 6); + + static const size_t headerSize = (3 + 3); + m_f->Read(buf, headerSize); + if (m_f->LastRead() != headerSize) + { + return wxGIF_INVFORMAT; + } if (memcmp(buf + 3, "89a", 3) < 0) - m_anim = FALSE; + { + m_anim = false; + } /* read logical screen descriptor block (LSDB) */ - m_f->Read(buf, 7); + static const size_t lsdbSize = (2 + 2 + 1 + 1 + 1); + m_f->Read(buf, lsdbSize); + if (m_f->LastRead() != lsdbSize) + { + return wxGIF_INVFORMAT; + } + m_screenw = buf[0] + 256 * buf[1]; m_screenh = buf[2] + 256 * buf[3]; @@ -564,7 +683,12 @@ int wxGIFDecoder::ReadGIF() m_background = buf[5]; ncolors = 2 << (buf[4] & 0x07); - m_f->Read(pal, 3 * ncolors); + size_t numBytes = 3 * ncolors; + m_f->Read(pal, numBytes); + if (m_f->LastRead() != numBytes) + { + return wxGIF_INVFORMAT; + } } /* transparent colour, disposal method and delay default to unused */ @@ -577,16 +701,33 @@ int wxGIFDecoder::ReadGIF() pprev = NULL; pimg = NULL; - bool done = FALSE; + bool done = false; while(!done) { type = (unsigned char)m_f->GetC(); + /* + If the end of file has been reached (or an error) and a ";" + (0x3B) hasn't been encountered yet, exit the loop. (Without this + check the while loop would loop endlessly.) Later on, in the next while + loop, the file will be treated as being truncated (But still + be decoded as far as possible). returning wxGIF_TRUNCATED is not + possible here since some init code is done after this loop. + */ + if (m_f->Eof())// || !m_f->IsOk()) + { + /* + type is set to some bogus value, so there's no + need to continue evaluating it. + */ + break; // Alternative : "return wxGIF_INVFORMAT;" + } + /* end of data? */ if (type == 0x3B) { - done = TRUE; + done = true; } else /* extension block? */ @@ -595,7 +736,13 @@ int wxGIFDecoder::ReadGIF() if (((unsigned char)m_f->GetC()) == 0xF9) /* graphics control extension, parse it */ { - m_f->Read(buf, 6); + static const size_t gceSize = 6; + m_f->Read(buf, gceSize); + if (m_f->LastRead() != gceSize) + { + Destroy(); + return wxGIF_INVFORMAT; + } /* read delay and convert from 1/100 of a second to ms */ delay = 10 * (buf[2] + 256 * buf[3]); @@ -613,6 +760,11 @@ int wxGIFDecoder::ReadGIF() while ((i = (unsigned char)m_f->GetC()) != 0) { m_f->SeekI(i, wxFromCurrent); + if (m_f->Eof()) + { + done = true; + break; + } } } } @@ -630,7 +782,14 @@ int wxGIFDecoder::ReadGIF() } /* fill in the data */ - m_f->Read(buf, 9); + static const size_t idbSize = (2 + 2 + 2 + 2 + 1); + m_f->Read(buf, idbSize); + if (m_f->LastRead() != idbSize) + { + Destroy(); + return wxGIF_INVFORMAT; + } + pimg->left = buf[0] + 256 * buf[1]; pimg->top = buf[2] + 256 * buf[3]; /* @@ -639,6 +798,13 @@ int wxGIFDecoder::ReadGIF() */ pimg->w = buf[4] + 256 * buf[5]; pimg->h = buf[6] + 256 * buf[7]; + + if (pimg->w == 0 || pimg->h == 0) + { + Destroy(); + return wxGIF_INVFORMAT; + } + interl = ((buf[8] & 0x40)? 1 : 0); size = pimg->w * pimg->h; @@ -664,38 +830,54 @@ int wxGIFDecoder::ReadGIF() if ((buf[8] & 0x80) == 0x80) { ncolors = 2 << (buf[8] & 0x07); - m_f->Read(pimg->pal, 3 * ncolors); + size_t numBytes = 3 * ncolors; + m_f->Read(pimg->pal, numBytes); + if (m_f->LastRead() != numBytes) + { + Destroy(); + return wxGIF_INVFORMAT; + } } else + { memcpy(pimg->pal, pal, 768); + } /* get initial code size from first byte in raster data */ bits = (unsigned char)m_f->GetC(); /* decode image */ - dgif(pimg, interl, bits); + int result = dgif(pimg, interl, bits); + if (result != wxGIF_OK) + { + Destroy(); + return result; + } m_nimages++; /* if this is not an animated GIF, exit after first image */ if (!m_anim) - done = TRUE; + done = true; } } - /* setup image pointers */ - if (m_nimages != 0) + if (m_nimages == 0) { - m_image = 1; - m_plast = pimg; - m_pimage = m_pfirst; + Destroy(); + return wxGIF_INVFORMAT; } + /* setup image pointers */ + m_image = 1; + m_plast = pimg; + m_pimage = m_pfirst; + /* try to read to the end of the stream */ while (type != 0x3B) { if (!m_f->IsOk()) return wxGIF_TRUNCATED; - + type = (unsigned char)m_f->GetC(); if (type == 0x21) @@ -712,13 +894,26 @@ int wxGIFDecoder::ReadGIF() else if (type == 0x2C) { /* image descriptor block */ - m_f->Read(buf, 9); + static const size_t idbSize = (2 + 2 + 2 + 2 + 1); + m_f->Read(buf, idbSize); + if (m_f->LastRead() != idbSize) + { + Destroy(); + return wxGIF_INVFORMAT; + } /* local color map */ if ((buf[8] & 0x80) == 0x80) { ncolors = 2 << (buf[8] & 0x07); - m_f->SeekI(3 * ncolors, wxFromCurrent); + wxFileOffset pos = m_f->TellI(); + wxFileOffset numBytes = 3 * ncolors; + m_f->SeekI(numBytes, wxFromCurrent); + if (m_f->TellI() != (pos + numBytes)) + { + Destroy(); + return wxGIF_INVFORMAT; + } } /* initial code size */