forked from Mirrorlandia_minetest/irrlicht
CImageLoaderBMP: add bound checks to RLE decompression
This commit is contained in:
parent
028cb8dbed
commit
4506d23dc3
@ -45,20 +45,32 @@ bool CImageLoaderBMP::isALoadableFileFormat(io::IReadFile* file) const
|
|||||||
return headerID == 0x4d42;
|
return headerID == 0x4d42;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UB-safe overflow check
|
||||||
|
static inline bool overflowCheck(const void *base, size_t offset, const void *end)
|
||||||
|
{
|
||||||
|
auto baseI = reinterpret_cast<uintptr_t>(base),
|
||||||
|
endI = reinterpret_cast<uintptr_t>(end);
|
||||||
|
return baseI > endI || offset >= (endI - baseI);
|
||||||
|
}
|
||||||
|
// check whether &p[0] to &p[_off - 1] can be accessed
|
||||||
|
#define CHECKP(_off) if ((_off) < 0 || overflowCheck(p, _off, pEnd)) goto exit
|
||||||
|
// same for d
|
||||||
|
#define CHECKD(_off) if ((_off) < 0 || overflowCheck(d, _off, destEnd)) goto exit
|
||||||
|
|
||||||
void CImageLoaderBMP::decompress8BitRLE(u8*& bmpData, s32 size, s32 width, s32 height, s32 pitch) const
|
void CImageLoaderBMP::decompress8BitRLE(u8*& bmpData, s32 size, s32 width, s32 height, s32 pitch) const
|
||||||
{
|
{
|
||||||
u8* p = bmpData;
|
u8* p = bmpData;
|
||||||
|
const u8* pEnd = bmpData + size;
|
||||||
u8* newBmp = new u8[(width+pitch)*height];
|
u8* newBmp = new u8[(width+pitch)*height];
|
||||||
u8* d = newBmp;
|
u8* d = newBmp;
|
||||||
u8* destEnd = newBmp + (width+pitch)*height;
|
const u8* destEnd = newBmp + (width+pitch)*height;
|
||||||
s32 line = 0;
|
s32 line = 0;
|
||||||
|
|
||||||
while (bmpData - p < size && d < destEnd)
|
while (p < pEnd && d < destEnd)
|
||||||
{
|
{
|
||||||
if (*p == 0)
|
if (*p == 0)
|
||||||
{
|
{
|
||||||
++p;
|
++p; CHECKP(1);
|
||||||
|
|
||||||
switch(*p)
|
switch(*p)
|
||||||
{
|
{
|
||||||
@ -68,29 +80,28 @@ void CImageLoaderBMP::decompress8BitRLE(u8*& bmpData, s32 size, s32 width, s32 h
|
|||||||
d = newBmp + (line*(width+pitch));
|
d = newBmp + (line*(width+pitch));
|
||||||
break;
|
break;
|
||||||
case 1: // end of bmp
|
case 1: // end of bmp
|
||||||
delete [] bmpData;
|
goto exit;
|
||||||
bmpData = newBmp;
|
|
||||||
return;
|
|
||||||
case 2:
|
case 2:
|
||||||
++p; d +=(u8)*p; // delta
|
++p; CHECKP(2);
|
||||||
++p; d += ((u8)*p)*(width+pitch);
|
d += (u8)*p; ++p; // delta
|
||||||
++p;
|
d += ((u8)*p)*(width+pitch); ++p;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// absolute mode
|
// absolute mode
|
||||||
s32 count = (u8)*p; ++p;
|
s32 count = (u8)*p; ++p;
|
||||||
s32 readAdditional = ((2-(count%2))%2);
|
s32 readAdditional = ((2-(count%2))%2);
|
||||||
s32 i;
|
|
||||||
|
|
||||||
for (i=0; i<count; ++i)
|
CHECKP(count); CHECKD(count);
|
||||||
|
for (s32 i=0; i<count; ++i)
|
||||||
{
|
{
|
||||||
*d = *p;
|
*d = *p;
|
||||||
++p;
|
++p;
|
||||||
++d;
|
++d;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i=0; i<readAdditional; ++i)
|
CHECKP(readAdditional);
|
||||||
|
for (s32 i=0; i<readAdditional; ++i)
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,7 +109,9 @@ void CImageLoaderBMP::decompress8BitRLE(u8*& bmpData, s32 size, s32 width, s32 h
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
s32 count = (u8)*p; ++p;
|
s32 count = (u8)*p; ++p;
|
||||||
|
CHECKP(1);
|
||||||
u8 color = *p; ++p;
|
u8 color = *p; ++p;
|
||||||
|
CHECKD(count);
|
||||||
for (s32 i=0; i<count; ++i)
|
for (s32 i=0; i<count; ++i)
|
||||||
{
|
{
|
||||||
*d = color;
|
*d = color;
|
||||||
@ -107,26 +120,37 @@ void CImageLoaderBMP::decompress8BitRLE(u8*& bmpData, s32 size, s32 width, s32 h
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
delete [] bmpData;
|
delete [] bmpData;
|
||||||
bmpData = newBmp;
|
bmpData = newBmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// how many bytes will be touched given the current state of decompress4BitRLE
|
||||||
|
static inline u32 shiftedCount(s32 count, s32 shift)
|
||||||
|
{
|
||||||
|
_IRR_DEBUG_BREAK_IF(count < 0)
|
||||||
|
u32 ret = count / 2;
|
||||||
|
if (shift == 0 || count % 2 == 1)
|
||||||
|
++ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void CImageLoaderBMP::decompress4BitRLE(u8*& bmpData, s32 size, s32 width, s32 height, s32 pitch) const
|
void CImageLoaderBMP::decompress4BitRLE(u8*& bmpData, s32 size, s32 width, s32 height, s32 pitch) const
|
||||||
{
|
{
|
||||||
s32 lineWidth = (width+1)/2+pitch;
|
const s32 lineWidth = (width+1)/2+pitch;
|
||||||
u8* p = bmpData;
|
u8* p = bmpData;
|
||||||
|
const u8* pEnd = bmpData + size;
|
||||||
u8* newBmp = new u8[lineWidth*height];
|
u8* newBmp = new u8[lineWidth*height];
|
||||||
u8* d = newBmp;
|
u8* d = newBmp;
|
||||||
u8* destEnd = newBmp + lineWidth*height;
|
const u8* destEnd = newBmp + lineWidth*height;
|
||||||
s32 line = 0;
|
s32 line = 0;
|
||||||
s32 shift = 4;
|
s32 shift = 4;
|
||||||
|
|
||||||
while (bmpData - p < size && d < destEnd)
|
while (p < pEnd && d < destEnd)
|
||||||
{
|
{
|
||||||
if (*p == 0)
|
if (*p == 0)
|
||||||
{
|
{
|
||||||
++p;
|
++p; CHECKP(1);
|
||||||
|
|
||||||
switch(*p)
|
switch(*p)
|
||||||
{
|
{
|
||||||
@ -137,12 +161,10 @@ void CImageLoaderBMP::decompress4BitRLE(u8*& bmpData, s32 size, s32 width, s32 h
|
|||||||
shift = 4;
|
shift = 4;
|
||||||
break;
|
break;
|
||||||
case 1: // end of bmp
|
case 1: // end of bmp
|
||||||
delete [] bmpData;
|
goto exit;
|
||||||
bmpData = newBmp;
|
|
||||||
return;
|
|
||||||
case 2:
|
case 2:
|
||||||
{
|
{
|
||||||
++p;
|
++p; CHECKP(2);
|
||||||
s32 x = (u8)*p; ++p;
|
s32 x = (u8)*p; ++p;
|
||||||
s32 y = (u8)*p; ++p;
|
s32 y = (u8)*p; ++p;
|
||||||
d += x/2 + y*lineWidth;
|
d += x/2 + y*lineWidth;
|
||||||
@ -155,15 +177,16 @@ void CImageLoaderBMP::decompress4BitRLE(u8*& bmpData, s32 size, s32 width, s32 h
|
|||||||
s32 count = (u8)*p; ++p;
|
s32 count = (u8)*p; ++p;
|
||||||
s32 readAdditional = ((2-((count)%2))%2);
|
s32 readAdditional = ((2-((count)%2))%2);
|
||||||
s32 readShift = 4;
|
s32 readShift = 4;
|
||||||
s32 i;
|
|
||||||
|
|
||||||
for (i=0; i<count; ++i)
|
CHECKP(shiftedCount(count, readShift));
|
||||||
|
CHECKD(shiftedCount(count, shift));
|
||||||
|
for (s32 i=0; i<count; ++i)
|
||||||
{
|
{
|
||||||
s32 color = (((u8)*p) >> readShift) & 0x0f;
|
s32 color = (((u8)*p) >> readShift) & 0x0f;
|
||||||
readShift -= 4;
|
readShift -= 4;
|
||||||
if (readShift < 0)
|
if (readShift < 0)
|
||||||
{
|
{
|
||||||
++*p;
|
++*p; // <- bug?
|
||||||
readShift = 4;
|
readShift = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +202,8 @@ void CImageLoaderBMP::decompress4BitRLE(u8*& bmpData, s32 size, s32 width, s32 h
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i=0; i<readAdditional; ++i)
|
CHECKP(readAdditional);
|
||||||
|
for (s32 i=0; i<readAdditional; ++i)
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -187,10 +211,12 @@ void CImageLoaderBMP::decompress4BitRLE(u8*& bmpData, s32 size, s32 width, s32 h
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
s32 count = (u8)*p; ++p;
|
s32 count = (u8)*p; ++p;
|
||||||
|
CHECKP(1);
|
||||||
s32 color1 = (u8)*p; color1 = color1 & 0x0f;
|
s32 color1 = (u8)*p; color1 = color1 & 0x0f;
|
||||||
s32 color2 = (u8)*p; color2 = (color2 >> 4) & 0x0f;
|
s32 color2 = (u8)*p; color2 = (color2 >> 4) & 0x0f;
|
||||||
++p;
|
++p;
|
||||||
|
|
||||||
|
CHECKD(shiftedCount(count, shift));
|
||||||
for (s32 i=0; i<count; ++i)
|
for (s32 i=0; i<count; ++i)
|
||||||
{
|
{
|
||||||
u8 mask = 0x0f << shift;
|
u8 mask = 0x0f << shift;
|
||||||
@ -207,11 +233,14 @@ void CImageLoaderBMP::decompress4BitRLE(u8*& bmpData, s32 size, s32 width, s32 h
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
delete [] bmpData;
|
delete [] bmpData;
|
||||||
bmpData = newBmp;
|
bmpData = newBmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef CHECKOFF
|
||||||
|
#undef CHECKP
|
||||||
|
#undef CHECKD
|
||||||
|
|
||||||
//! creates a surface from the file
|
//! creates a surface from the file
|
||||||
IImage* CImageLoaderBMP::loadImage(io::IReadFile* file) const
|
IImage* CImageLoaderBMP::loadImage(io::IReadFile* file) const
|
||||||
|
Loading…
Reference in New Issue
Block a user