Fix SGI foramt reader CVE-2017-2901

Integer Overflow Code Execution Vulnerability.

Reader no longer crashes on corrupt images (from own fuzz testing).
This commit is contained in:
Campbell Barton
2017-09-17 16:22:56 +10:00
parent b884188f8a
commit 829916f4e5

View File

@@ -109,10 +109,14 @@ typedef struct MFileOffset {
uint _file_offset;
} MFileOffset;
#define MFILE_DATA(inf) ((void)0, (inf)->_file_data + (inf)->_file_offset)
#define MFILE_DATA(inf) ((void)0, ((inf)->_file_data + (inf)->_file_offset))
#define MFILE_STEP(inf, step) { (inf)->_file_offset += step; } ((void)0)
#define MFILE_SEEK(inf, pos) { (inf)->_file_offset = pos; } ((void)0)
/* error flags */
#define DIRTY_FLAG_EOF (1 << 0)
#define DIRTY_FLAG_ENCODING (1 << 1)
/* funcs */
static void readheader(MFileOffset *inf, IMAGE *image);
static int writeheader(FILE *outf, IMAGE *image);
@@ -124,8 +128,8 @@ static int putlong(FILE *outf, uint val);
static int writetab(FILE *outf, uint *tab, int len);
static void readtab(MFileOffset *inf, uint *tab, int len);
static void expandrow(uchar *optr, const uchar *iptr, int z);
static void expandrow2(float *optr, const uchar *iptr, int z);
static int expandrow(uchar *optr, const uchar *optr_end, const uchar *iptr, const uchar *iptr_end, int z);
static int expandrow2(float *optr, const float *optr_end, const uchar *iptr, const uchar *iptr_end, int z);
static void interleaverow(uchar *lptr, const uchar *cptr, int z, int n);
static void interleaverow2(float *lptr, const uchar *cptr, int z, int n);
static int compressrow(uchar *lbuf, uchar *rlebuf, int z, int cnt);
@@ -265,40 +269,46 @@ struct ImBuf *imb_loadiris(const uchar *mem, size_t size, int flags, char colors
float *fbase, *fptr = NULL;
uint *zbase, *zptr;
const uchar *rledat;
uint *starttab, *lengthtab;
const uchar *mem_end = mem + size;
MFileOffset _inf_data = {mem, 0}, *inf = &_inf_data;
IMAGE image;
int x, y, z, tablen;
int xsize, ysize, zsize;
int bpp, rle, cur, badorder;
ImBuf *ibuf;
uchar dirty_flag = 0;
(void)size; /* unused */
if (!imb_is_a_iris(mem)) return NULL;
if (size < HEADER_SIZE) {
return NULL;
}
if (!imb_is_a_iris(mem)) {
return NULL;
}
/* OCIO_TODO: only tested with 1 byte per pixel, not sure how to test with other settings */
colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE);
/*printf("new iris\n");*/
readheader(inf, &image);
if (image.imagic != IMAGIC) {
fprintf(stderr, "longimagedata: bad magic number in image file\n");
return(NULL);
}
rle = ISRLE(image.type);
bpp = BPP(image.type);
if (bpp != 1 && bpp != 2) {
fprintf(stderr, "longimagedata: image must have 1 or 2 byte per pix chan\n");
return(NULL);
}
xsize = image.xsize;
ysize = image.ysize;
zsize = image.zsize;
if ((uint)image.zsize > 8) {
fprintf(stderr, "longimagedata: channels over 8 not supported\n");
return(NULL);
}
const int xsize = image.xsize;
const int ysize = image.ysize;
const int zsize = image.zsize;
if (flags & IB_test) {
ibuf = IMB_allocImBuf(image.xsize, image.ysize, 8 * image.zsize, 0);
if (ibuf) ibuf->ftype = IMB_FTYPE_IMAGIC;
@@ -306,12 +316,17 @@ struct ImBuf *imb_loadiris(const uchar *mem, size_t size, int flags, char colors
}
if (rle) {
tablen = ysize * zsize * sizeof(int);
starttab = (uint *)MEM_mallocN(tablen, "iris starttab");
lengthtab = (uint *)MEM_mallocN(tablen, "iris endtab");
MFILE_SEEK(inf, HEADER_SIZE);
uint *starttab = MEM_mallocN(tablen, "iris starttab");
uint *lengthtab = MEM_mallocN(tablen, "iris endtab");
#define MFILE_CAPACITY_AT_PTR_OK_OR_FAIL(p) \
if (UNLIKELY((p) > mem_end)) { dirty_flag |= DIRTY_FLAG_EOF; goto fail_rle; } ((void)0)
MFILE_CAPACITY_AT_PTR_OK_OR_FAIL(MFILE_DATA(inf) + ((4 * 2) * tablen));
readtab(inf, starttab, tablen);
readtab(inf, lengthtab, tablen);
@@ -344,9 +359,11 @@ struct ImBuf *imb_loadiris(const uchar *mem, size_t size, int flags, char colors
MFILE_SEEK(inf, starttab[y + z * ysize]);
rledat = MFILE_DATA(inf);
MFILE_STEP(inf, lengthtab[y + z * ysize]);
expandrow((uchar *)lptr, rledat, 3 - z);
lptr += xsize;
const uchar *rledat_next = MFILE_DATA(inf);
uint *lptr_next = lptr + xsize;
MFILE_CAPACITY_AT_PTR_OK_OR_FAIL(rledat_next);
dirty_flag |= expandrow((uchar *)lptr, (uchar *)lptr_next, rledat, rledat_next, 3 - z);
lptr = lptr_next;
}
}
}
@@ -354,17 +371,25 @@ struct ImBuf *imb_loadiris(const uchar *mem, size_t size, int flags, char colors
lptr = base;
zptr = zbase;
for (y = 0; y < ysize; y++) {
uint *lptr_next = lptr + xsize;
uint *zptr_next = zptr + xsize;
for (z = 0; z < zsize; z++) {
MFILE_SEEK(inf, starttab[y + z * ysize]);
rledat = MFILE_DATA(inf);
MFILE_STEP(inf, lengthtab[y + z * ysize]);
if (z < 4) expandrow((uchar *)lptr, rledat, 3 - z);
else if (z < 8) expandrow((uchar *)zptr, rledat, 7 - z);
const uchar *rledat_next = MFILE_DATA(inf);
MFILE_CAPACITY_AT_PTR_OK_OR_FAIL(rledat_next);
if (z < 4) {
dirty_flag |= expandrow((uchar *)lptr, (uchar *)lptr_next, rledat, rledat_next, 3 - z);
}
else if (z < 8) {
dirty_flag |= expandrow((uchar *)zptr, (uchar *)zptr_next, rledat, rledat_next, 7 - z);
}
}
lptr += xsize;
zptr += xsize;
lptr = lptr_next;
zptr = zptr_next;
}
}
@@ -383,14 +408,17 @@ struct ImBuf *imb_loadiris(const uchar *mem, size_t size, int flags, char colors
MFILE_SEEK(inf, starttab[y + z * ysize]);
rledat = MFILE_DATA(inf);
MFILE_STEP(inf, lengthtab[y + z * ysize]);
expandrow2(fptr, rledat, 3 - z);
fptr += xsize * 4;
const uchar *rledat_next = MFILE_DATA(inf);
MFILE_CAPACITY_AT_PTR_OK_OR_FAIL(rledat_next);
float *fptr_next = fptr + (xsize * 4);
dirty_flag |= expandrow2(fptr, fptr_next, rledat, rledat_next, 3 - z);
fptr = fptr_next;
}
}
}
else {
fptr = fbase;
float *fptr_next = fptr + (xsize * 4);
for (y = 0; y < ysize; y++) {
@@ -398,20 +426,24 @@ struct ImBuf *imb_loadiris(const uchar *mem, size_t size, int flags, char colors
MFILE_SEEK(inf, starttab[y + z * ysize]);
rledat = MFILE_DATA(inf);
MFILE_STEP(inf, lengthtab[y + z * ysize]);
expandrow2(fptr, rledat, 3 - z);
const uchar *rledat_next = MFILE_DATA(inf);
MFILE_CAPACITY_AT_PTR_OK_OR_FAIL(rledat_next);
dirty_flag |= expandrow2(fptr, fptr_next, rledat, rledat_next, 3 - z);
}
fptr += xsize * 4;
fptr = fptr_next;
}
}
}
#undef MFILE_CAPACITY_AT_PTR_OK_OR_FAIL
fail_rle:
MEM_freeN(starttab);
MEM_freeN(lengthtab);
}
else {
#define MFILE_CAPACITY_AT_PTR_OK_OR_FAIL(p) \
if (UNLIKELY((p) > mem_end)) { dirty_flag |= DIRTY_FLAG_EOF; goto fail_uncompressed; } ((void)0)
if (bpp == 1) {
ibuf = IMB_allocImBuf(xsize, ysize, 8 * zsize, IB_rect);
@@ -427,12 +459,13 @@ struct ImBuf *imb_loadiris(const uchar *mem, size_t size, int flags, char colors
if (z < 4) lptr = base;
else if (z < 8) lptr = zbase;
for (y = 0; y < ysize; y++) {
interleaverow((uchar *)lptr, rledat, 3 - z, xsize);
rledat += xsize;
for (y = 0; y < ysize; y++) {
const uchar *rledat_next = rledat + xsize;
const int z_ofs = 3 - z;
MFILE_CAPACITY_AT_PTR_OK_OR_FAIL(rledat_next + z_ofs);
interleaverow((uchar *)lptr, rledat, z_ofs, xsize);
rledat = rledat_next;
lptr += xsize;
}
}
@@ -450,20 +483,23 @@ struct ImBuf *imb_loadiris(const uchar *mem, size_t size, int flags, char colors
for (z = 0; z < zsize; z++) {
fptr = fbase;
for (y = 0; y < ysize; y++) {
interleaverow2(fptr, rledat, 3 - z, xsize);
rledat += xsize * 2;
for (y = 0; y < ysize; y++) {
const uchar *rledat_next = rledat + xsize * 2;
const int z_ofs = 3 - z;
MFILE_CAPACITY_AT_PTR_OK_OR_FAIL(rledat_next + z_ofs);
interleaverow2(fptr, rledat, z_ofs, xsize);
rledat = rledat_next;
fptr += xsize * 4;
}
}
}
#undef MFILE_CAPACITY_AT_PTR_OK_OR_FAIL
fail_uncompressed:
(void)0;
}
if (bpp == 1) {
uchar *rect;
@@ -528,6 +564,9 @@ struct ImBuf *imb_loadiris(const uchar *mem, size_t size, int flags, char colors
}
if (dirty_flag) {
fprintf(stderr, "longimagedata: corrupt file content (%d)\n", dirty_flag);
}
ibuf->ftype = IMB_FTYPE_IMAGIC;
test_endian_zbuf(ibuf);
@@ -560,19 +599,34 @@ static void interleaverow2(float *lptr, const uchar *cptr, int z, int n)
}
}
static void expandrow2(float *optr, const uchar *iptr, int z)
static int expandrow2(
float *optr, const float *optr_end,
const uchar *iptr, const uchar *iptr_end, int z)
{
ushort pixel, count;
float pixel_f;
#define EXPAND_CAPACITY_AT_INPUT_OK_OR_FAIL(iptr_next) \
if (UNLIKELY(iptr_next > iptr_end)) { goto fail; }
#define EXPAND_CAPACITY_AT_OUTPUT_OK_OR_FAIL(optr_next) \
if (UNLIKELY(optr_next > optr_end)) { goto fail; }
optr += z;
optr_end += z;
while (1) {
const uchar *iptr_next = iptr + 2;
EXPAND_CAPACITY_AT_INPUT_OK_OR_FAIL(iptr_next);
pixel = (iptr[0] << 8) | (iptr[1] << 0);
iptr += 2;
iptr = iptr_next;
if (!(count = (pixel & 0x7f)) )
return;
return false;
const float *optr_next = optr + count;
EXPAND_CAPACITY_AT_OUTPUT_OK_OR_FAIL(optr_next);
if (pixel & 0x80) {
iptr_next = iptr + (count * 2);
EXPAND_CAPACITY_AT_INPUT_OK_OR_FAIL(iptr_next);
while (count >= 8) {
optr[0 * 4] = ((iptr[0] << 8) | (iptr[1] << 0)) / (float)0xFFFF;
optr[1 * 4] = ((iptr[2] << 8) | (iptr[3] << 0)) / (float)0xFFFF;
@@ -591,10 +645,13 @@ static void expandrow2(float *optr, const uchar *iptr, int z)
iptr += 2;
optr += 4;
}
BLI_assert(iptr == iptr_next);
}
else {
iptr_next = iptr + 2;
EXPAND_CAPACITY_AT_INPUT_OK_OR_FAIL(iptr_next);
pixel_f = ((iptr[0] << 8) | (iptr[1] << 0)) / (float)0xFFFF;
iptr += 2;
iptr = iptr_next;
while (count >= 8) {
optr[0 * 4] = pixel_f;
@@ -612,20 +669,45 @@ static void expandrow2(float *optr, const uchar *iptr, int z)
*optr = pixel_f;
optr += 4;
}
BLI_assert(iptr == iptr_next);
}
BLI_assert(optr == optr_next);
}
return false;
#undef EXPAND_CAPACITY_AT_INPUT_OK_OR_FAIL
#undef EXPAND_CAPACITY_AT_OUTPUT_OK_OR_FAIL
fail:
return DIRTY_FLAG_ENCODING;
}
static void expandrow(uchar *optr, const uchar *iptr, int z)
static int expandrow(
uchar *optr, const uchar *optr_end,
const uchar *iptr, const uchar *iptr_end, int z)
{
uchar pixel, count;
#define EXPAND_CAPACITY_AT_INPUT_OK_OR_FAIL(iptr_next) \
if (UNLIKELY(iptr_next > iptr_end)) { goto fail; }
#define EXPAND_CAPACITY_AT_OUTPUT_OK_OR_FAIL(optr_next) \
if (UNLIKELY(optr_next > optr_end)) { goto fail; }
optr += z;
optr_end += z;
while (1) {
pixel = *iptr++;
const uchar *iptr_next = iptr + 1;
EXPAND_CAPACITY_AT_INPUT_OK_OR_FAIL(iptr_next);
pixel = *iptr;
iptr = iptr_next;
if (!(count = (pixel & 0x7f)) )
return;
return false;
const uchar *optr_next = optr + ((int)count * 4);
EXPAND_CAPACITY_AT_OUTPUT_OK_OR_FAIL(optr_next);
if (pixel & 0x80) {
iptr_next = iptr + count;
EXPAND_CAPACITY_AT_INPUT_OK_OR_FAIL(iptr_next);
while (count >= 8) {
optr[0 * 4] = iptr[0];
optr[1 * 4] = iptr[1];
@@ -643,8 +725,11 @@ static void expandrow(uchar *optr, const uchar *iptr, int z)
*optr = *iptr++;
optr += 4;
}
BLI_assert(iptr == iptr_next);
}
else {
iptr_next = iptr + 1;
EXPAND_CAPACITY_AT_INPUT_OK_OR_FAIL(iptr_next);
pixel = *iptr++;
while (count >= 8) {
optr[0 * 4] = pixel;
@@ -662,8 +747,17 @@ static void expandrow(uchar *optr, const uchar *iptr, int z)
*optr = pixel;
optr += 4;
}
BLI_assert(iptr == iptr_next);
}
BLI_assert(optr == optr_next);
}
return false;
#undef EXPAND_CAPACITY_AT_INPUT_OK_OR_FAIL
#undef EXPAND_CAPACITY_AT_OUTPUT_OK_OR_FAIL
fail:
return DIRTY_FLAG_ENCODING;
}
/*