BE-164-cve-2021-25289

Change release notes
Update docs
Add test image
Merge in the changes to TiffDecode.c
This commit is contained in:
Frederick Price 2023-03-31 14:58:40 -04:00
parent 3a855cb647
commit 99399058ff
4 changed files with 660 additions and 628 deletions

View File

@ -2,12 +2,14 @@
Changelog (Pillow) Changelog (Pillow)
================== ==================
6.2.2.5 (date TBD) 6.2.2.5 (Date TBD)
------------------ ------------------
- Fix CVE-2020-35654 - Fix CVE CVE-2021-25289: An issue was discovered in Pillow before 8.1.1.
[rickprice] TiffDecode has a heap-based buffer overflow when decoding crafted YCbCr files
because of certain interpretation conflicts with LibTIFF in RGBA mode. NOTE:
this issue exists because of an incomplete fix for CVE-2020-35654.
[rickprice]
6.2.2.4 (2023-03-29) 6.2.2.4 (2023-03-29)
------------------ ------------------

Binary file not shown.

View File

@ -1,4 +1,4 @@
6.2.2.4 6.2.2.5
------- -------
Security Security
@ -6,6 +6,8 @@ Security
This release addresses several critical CVEs. This release addresses several critical CVEs.
:cve:`CVE-2020-35654`: In Pillow before 8.1.0, TiffDecode has a heap-based buffer overflow when decoding crafted YCbCr files because of certain interpretation conflicts with LibTIFF in RGBA mode. :cve:`CVE-2021-25289`: An issue was discovered in Pillow before 8.1.1.
TiffDecode has a heap-based buffer overflow when decoding crafted YCbCr files
because of certain interpretation conflicts with LibTIFF in RGBA mode. NOTE:
this issue exists because of an incomplete fix for CVE-2020-35654.

View File

@ -20,10 +20,12 @@
#include "TiffDecode.h" #include "TiffDecode.h"
/* Convert C file descriptor to WinApi HFILE if LibTiff was compiled with tif_win32.c /* Convert C file descriptor to WinApi HFILE if LibTiff was compiled with
* tif_win32.c
* *
* This cast is safe, as the top 32-bits of HFILE are guaranteed to be zero, * This cast is safe, as the top 32-bits of HFILE are guaranteed to be zero,
* see https://docs.microsoft.com/en-us/windows/win32/winprog64/interprocess-communication * see
* https://docs.microsoft.com/en-us/windows/win32/winprog64/interprocess-communication
*/ */
#ifndef USE_WIN32_FILEIO #ifndef USE_WIN32_FILEIO
#define fd_to_tiff_fd(fd) (fd) #define fd_to_tiff_fd(fd) (fd)
@ -31,9 +33,10 @@
#define fd_to_tiff_fd(fd) ((int)_get_osfhandle(fd)) #define fd_to_tiff_fd(fd) ((int)_get_osfhandle(fd))
#endif #endif
void dump_state(const TIFFSTATE *state){ void dump_state(const TIFFSTATE *state) {
TRACE(("State: Location %u size %d eof %d data: %p ifd: %d\n", (uint)state->loc, TRACE(("State: Location %u size %d eof %d data: %p ifd: %d\n",
(int)state->size, (uint)state->eof, state->data, state->ifd)); (uint)state->loc, (int)state->size, (uint)state->eof, state->data,
state->ifd));
} }
/* /*
@ -48,16 +51,18 @@ tsize_t _tiffReadProc(thandle_t hdata, tdata_t buf, tsize_t size) {
dump_state(state); dump_state(state);
if (state->loc > state->eof) { if (state->loc > state->eof) {
TIFFError("_tiffReadProc", "Invalid Read at loc %d, eof: %d", state->loc, state->eof); TIFFError("_tiffReadProc", "Invalid Read at loc %d, eof: %d", state->loc,
state->eof);
return 0; return 0;
} }
to_read = min(size, min(state->size, (tsize_t)state->eof) - (tsize_t)state->loc); to_read =
min(size, min(state->size, (tsize_t)state->eof) - (tsize_t)state->loc);
TRACE(("to_read: %d\n", (int)to_read)); TRACE(("to_read: %d\n", (int)to_read));
_TIFFmemcpy(buf, (UINT8 *)state->data + state->loc, to_read); _TIFFmemcpy(buf, (UINT8 *)state->data + state->loc, to_read);
state->loc += (toff_t)to_read; state->loc += (toff_t)to_read;
TRACE( ("location: %u\n", (uint)state->loc)); TRACE(("location: %u\n", (uint)state->loc));
return to_read; return to_read;
} }
@ -69,14 +74,14 @@ tsize_t _tiffWriteProc(thandle_t hdata, tdata_t buf, tsize_t size) {
dump_state(state); dump_state(state);
to_write = min(size, state->size - (tsize_t)state->loc); to_write = min(size, state->size - (tsize_t)state->loc);
if (state->flrealloc && size>to_write) { if (state->flrealloc && size > to_write) {
tdata_t new_data; tdata_t new_data;
tsize_t newsize=state->size; tsize_t newsize = state->size;
while (newsize < (size + state->size)) { while (newsize < (size + state->size)) {
if (newsize > INT_MAX - 64*1024){ if (newsize > INT_MAX - 64 * 1024) {
return 0; return 0;
} }
newsize += 64*1024; newsize += 64 * 1024;
// newsize*=2; // UNDONE, by 64k chunks? // newsize*=2; // UNDONE, by 64k chunks?
} }
TRACE(("Reallocing in write to %d bytes\n", (int)newsize)); TRACE(("Reallocing in write to %d bytes\n", (int)newsize));
@ -130,7 +135,6 @@ int _tiffCloseProc(thandle_t hdata) {
return 0; return 0;
} }
toff_t _tiffSizeProc(thandle_t hdata) { toff_t _tiffSizeProc(thandle_t hdata) {
TIFFSTATE *state = (TIFFSTATE *)hdata; TIFFSTATE *state = (TIFFSTATE *)hdata;
@ -140,7 +144,7 @@ toff_t _tiffSizeProc(thandle_t hdata) {
return (toff_t)state->size; return (toff_t)state->size;
} }
int _tiffMapProc(thandle_t hdata, tdata_t* pbase, toff_t* psize) { int _tiffMapProc(thandle_t hdata, tdata_t *pbase, toff_t *psize) {
TIFFSTATE *state = (TIFFSTATE *)hdata; TIFFSTATE *state = (TIFFSTATE *)hdata;
TRACE(("_tiffMapProc input size: %u, data: %p\n", (uint)*psize, *pbase)); TRACE(("_tiffMapProc input size: %u, data: %p\n", (uint)*psize, *pbase));
@ -152,14 +156,18 @@ int _tiffMapProc(thandle_t hdata, tdata_t* pbase, toff_t* psize) {
return (1); return (1);
} }
int _tiffNullMapProc(thandle_t hdata, tdata_t* pbase, toff_t* psize) { int _tiffNullMapProc(thandle_t hdata, tdata_t *pbase, toff_t *psize) {
(void) hdata; (void) pbase; (void) psize; (void)hdata;
(void)pbase;
(void)psize;
return (0); return (0);
} }
void _tiffUnmapProc(thandle_t hdata, tdata_t base, toff_t size) { void _tiffUnmapProc(thandle_t hdata, tdata_t base, toff_t size) {
TRACE(("_tiffUnMapProc\n")); TRACE(("_tiffUnMapProc\n"));
(void) hdata; (void) base; (void) size; (void)hdata;
(void)base;
(void)size;
} }
int ImagingLibTiffInit(ImagingCodecState state, int fp, uint32 offset) { int ImagingLibTiffInit(ImagingCodecState state, int fp, uint32 offset) {
@ -167,10 +175,10 @@ int ImagingLibTiffInit(ImagingCodecState state, int fp, uint32 offset) {
TRACE(("initing libtiff\n")); TRACE(("initing libtiff\n"));
TRACE(("filepointer: %d \n", fp)); TRACE(("filepointer: %d \n", fp));
TRACE(("State: count %d, state %d, x %d, y %d, ystep %d\n", state->count, state->state, TRACE(("State: count %d, state %d, x %d, y %d, ystep %d\n", state->count,
state->x, state->y, state->ystep)); state->state, state->x, state->y, state->ystep));
TRACE(("State: xsize %d, ysize %d, xoff %d, yoff %d \n", state->xsize, state->ysize, TRACE(("State: xsize %d, ysize %d, xoff %d, yoff %d \n", state->xsize,
state->xoff, state->yoff)); state->ysize, state->xoff, state->yoff));
TRACE(("State: bits %d, bytes %d \n", state->bits, state->bytes)); TRACE(("State: bits %d, bytes %d \n", state->bits, state->bytes));
TRACE(("State: context %p \n", state->context)); TRACE(("State: context %p \n", state->context));
@ -184,7 +192,6 @@ int ImagingLibTiffInit(ImagingCodecState state, int fp, uint32 offset) {
return 1; return 1;
} }
int _decodeStripYCbCr(Imaging im, ImagingCodecState state, TIFF *tiff) { int _decodeStripYCbCr(Imaging im, ImagingCodecState state, TIFF *tiff) {
// To avoid dealing with YCbCr subsampling, let libtiff handle it // To avoid dealing with YCbCr subsampling, let libtiff handle it
// Use a TIFFRGBAImage wrapping the tiff image, and let libtiff handle // Use a TIFFRGBAImage wrapping the tiff image, and let libtiff handle
@ -204,7 +211,8 @@ int _decodeStripYCbCr(Imaging im, ImagingCodecState state, TIFF *tiff) {
} }
TRACE(("RowsPerStrip: %u \n", rows_per_strip)); TRACE(("RowsPerStrip: %u \n", rows_per_strip));
if (!(TIFFRGBAImageOK(tiff, emsg) && TIFFRGBAImageBegin(&img, tiff, 0, emsg))) { if (!(TIFFRGBAImageOK(tiff, emsg) &&
TIFFRGBAImageBegin(&img, tiff, 0, emsg))) {
TRACE(("Decode error, msg: %s", emsg)); TRACE(("Decode error, msg: %s", emsg));
state->errcode = IMAGING_CODEC_BROKEN; state->errcode = IMAGING_CODEC_BROKEN;
// nothing to clean up, just return // nothing to clean up, just return
@ -215,8 +223,8 @@ int _decodeStripYCbCr(Imaging im, ImagingCodecState state, TIFF *tiff) {
img.col_offset = 0; img.col_offset = 0;
if (state->xsize != img.width || state->ysize != img.height) { if (state->xsize != img.width || state->ysize != img.height) {
TRACE(("Inconsistent Image Error: %d =? %d, %d =? %d", TRACE(("Inconsistent Image Error: %d =? %d, %d =? %d", state->xsize,
state->xsize, img.width, state->ysize, img.height)); img.width, state->ysize, img.height));
state->errcode = IMAGING_CODEC_BROKEN; state->errcode = IMAGING_CODEC_BROKEN;
goto decodeycbcr_err; goto decodeycbcr_err;
} }
@ -242,7 +250,7 @@ int _decodeStripYCbCr(Imaging im, ImagingCodecState state, TIFF *tiff) {
/* realloc to fit whole strip */ /* realloc to fit whole strip */
/* malloc check above */ /* malloc check above */
new_data = realloc (state->buffer, state->bytes); new_data = realloc(state->buffer, state->bytes);
if (!new_data) { if (!new_data) {
state->errcode = IMAGING_CODEC_MEMORY; state->errcode = IMAGING_CODEC_MEMORY;
goto decodeycbcr_err; goto decodeycbcr_err;
@ -254,8 +262,9 @@ int _decodeStripYCbCr(Imaging im, ImagingCodecState state, TIFF *tiff) {
img.row_offset = state->y; img.row_offset = state->y;
rows_to_read = min(rows_per_strip, img.height - state->y); rows_to_read = min(rows_per_strip, img.height - state->y);
if (!TIFFRGBAImageGet(&img, (UINT32 *)state->buffer, img.width, rows_to_read)) { if (!TIFFRGBAImageGet(&img, (UINT32 *)state->buffer, img.width,
TRACE(("Decode Error, y: %d\n", state->y )); rows_to_read)) {
TRACE(("Decode Error, y: %d\n", state->y));
state->errcode = IMAGING_CODEC_BROKEN; state->errcode = IMAGING_CODEC_BROKEN;
goto decodeycbcr_err; goto decodeycbcr_err;
} }
@ -263,20 +272,22 @@ int _decodeStripYCbCr(Imaging im, ImagingCodecState state, TIFF *tiff) {
TRACE(("Decoded strip for row %d \n", state->y)); TRACE(("Decoded strip for row %d \n", state->y));
// iterate over each row in the strip and stuff data into image // iterate over each row in the strip and stuff data into image
for (strip_row = 0; strip_row < min((INT32) rows_per_strip, state->ysize - state->y); strip_row++) { for (strip_row = 0;
strip_row < min((INT32)rows_per_strip, state->ysize - state->y);
strip_row++) {
TRACE(("Writing data into line %d ; \n", state->y + strip_row)); TRACE(("Writing data into line %d ; \n", state->y + strip_row));
// UINT8 * bbb = state->buffer + strip_row * (state->bytes / rows_per_strip); // UINT8 * bbb = state->buffer + strip_row * (state->bytes /
// TRACE(("chars: %x %x %x %x\n", ((UINT8 *)bbb)[0], ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3])); // rows_per_strip); TRACE(("chars: %x %x %x %x\n", ((UINT8 *)bbb)[0],
// ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3]));
state->shuffle((UINT8*) im->image[state->y + state->yoff + strip_row] + state->shuffle((UINT8 *)im->image[state->y + state->yoff + strip_row] +
state->xoff * im->pixelsize, state->xoff * im->pixelsize,
state->buffer + strip_row * row_byte_size, state->buffer + strip_row * row_byte_size, state->xsize);
state->xsize);
} }
} }
decodeycbcr_err: decodeycbcr_err:
TIFFRGBAImageEnd(&img); TIFFRGBAImageEnd(&img);
if (state->errcode != 0) { if (state->errcode != 0) {
return -1; return -1;
@ -296,12 +307,14 @@ int _decodeStrip(Imaging im, ImagingCodecState state, TIFF *tiff) {
} }
TRACE(("RowsPerStrip: %u \n", rows_per_strip)); TRACE(("RowsPerStrip: %u \n", rows_per_strip));
// We could use TIFFStripSize, but for YCbCr data it returns subsampled data size // We could use TIFFStripSize, but for YCbCr data it returns subsampled data
// size
row_byte_size = (state->xsize * state->bits + 7) / 8; row_byte_size = (state->xsize * state->bits + 7) / 8;
/* overflow check for realloc */ /* overflow check for realloc */
if (INT_MAX / row_byte_size < rows_per_strip) { if (INT_MAX / row_byte_size < rows_per_strip) {
state->errcode = IMAGING_CODEC_MEMORY; state->errcode = IMAGING_CODEC_MEMORY;
TIFFClose(tiff);
return -1; return -1;
} }
@ -310,74 +323,85 @@ int _decodeStrip(Imaging im, ImagingCodecState state, TIFF *tiff) {
TRACE(("StripSize: %d \n", state->bytes)); TRACE(("StripSize: %d \n", state->bytes));
if (TIFFStripSize(tiff) > state->bytes) { if (TIFFStripSize(tiff) > state->bytes) {
// If the strip size as expected by LibTiff isn't what we're expecting, abort. // If the strip size as expected by LibTiff isn't what we're expecting,
// man: TIFFStripSize returns the equivalent size for a strip of data as it would be returned in a // abort. man: TIFFStripSize returns the equivalent size for a strip of
// data as it would be returned in a
// call to TIFFReadEncodedStrip ... // call to TIFFReadEncodedStrip ...
state->errcode = IMAGING_CODEC_MEMORY; state->errcode = IMAGING_CODEC_MEMORY;
TIFFClose(tiff);
return -1; return -1;
} }
/* realloc to fit whole strip */ /* realloc to fit whole strip */
/* malloc check above */ /* malloc check above */
new_data = realloc (state->buffer, state->bytes); new_data = realloc(state->buffer, state->bytes);
if (!new_data) { if (!new_data) {
state->errcode = IMAGING_CODEC_MEMORY; state->errcode = IMAGING_CODEC_MEMORY;
TIFFClose(tiff);
return -1; return -1;
} }
state->buffer = new_data; state->buffer = new_data;
for (; state->y < state->ysize; state->y += rows_per_strip) { for (; state->y < state->ysize; state->y += rows_per_strip) {
if (TIFFReadEncodedStrip(tiff, TIFFComputeStrip(tiff, state->y, 0), (tdata_t)state->buffer, -1) == -1) { if (TIFFReadEncodedStrip(tiff, TIFFComputeStrip(tiff, state->y, 0),
(tdata_t)state->buffer, -1) == -1) {
TRACE(("Decode Error, strip %d\n", TIFFComputeStrip(tiff, state->y, 0))); TRACE(("Decode Error, strip %d\n", TIFFComputeStrip(tiff, state->y, 0)));
state->errcode = IMAGING_CODEC_BROKEN; state->errcode = IMAGING_CODEC_BROKEN;
TIFFClose(tiff);
return -1; return -1;
} }
TRACE(("Decoded strip for row %d \n", state->y)); TRACE(("Decoded strip for row %d \n", state->y));
// iterate over each row in the strip and stuff data into image // iterate over each row in the strip and stuff data into image
for (strip_row = 0; strip_row < min((INT32) rows_per_strip, state->ysize - state->y); strip_row++) { for (strip_row = 0;
strip_row < min((INT32)rows_per_strip, state->ysize - state->y);
strip_row++) {
TRACE(("Writing data into line %d ; \n", state->y + strip_row)); TRACE(("Writing data into line %d ; \n", state->y + strip_row));
// UINT8 * bbb = state->buffer + strip_row * (state->bytes / rows_per_strip); // UINT8 * bbb = state->buffer + strip_row * (state->bytes /
// TRACE(("chars: %x %x %x %x\n", ((UINT8 *)bbb)[0], ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3])); // rows_per_strip); TRACE(("chars: %x %x %x %x\n", ((UINT8 *)bbb)[0],
// ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3]));
state->shuffle((UINT8*) im->image[state->y + state->yoff + strip_row] + state->shuffle((UINT8 *)im->image[state->y + state->yoff + strip_row] +
state->xoff * im->pixelsize, state->xoff * im->pixelsize,
state->buffer + strip_row * row_byte_size, state->buffer + strip_row * row_byte_size, state->xsize);
state->xsize);
} }
} }
return 0; return 0;
} }
int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, Py_ssize_t bytes) { int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8 *buffer,
Py_ssize_t bytes) {
TIFFSTATE *clientstate = (TIFFSTATE *)state->context; TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
char *filename = "tempfile.tif"; char *filename = "tempfile.tif";
char *mode = "r"; char *mode = "r";
TIFF *tiff; TIFF *tiff;
uint16 photometric = 0; // init to not PHOTOMETRIC_YCBCR uint16 photometric = 0; // init to not PHOTOMETRIC_YCBCR
int isYCbCr = 0; int isYCbCr = 0;
int ret;
/* buffer is the encoded file, bytes is the length of the encoded file */ /* buffer is the encoded file, bytes is the length of the encoded file */
/* it all ends up in state->buffer, which is a uint8* from Imaging.h */ /* it all ends up in state->buffer, which is a uint8* from Imaging.h */
TRACE(("in decoder: bytes %d\n", bytes)); TRACE(("in decoder: bytes %d\n", bytes));
TRACE(("State: count %d, state %d, x %d, y %d, ystep %d\n", state->count, state->state, TRACE(("State: count %d, state %d, x %d, y %d, ystep %d\n", state->count,
state->x, state->y, state->ystep)); state->state, state->x, state->y, state->ystep));
TRACE(("State: xsize %d, ysize %d, xoff %d, yoff %d \n", state->xsize, state->ysize, TRACE(("State: xsize %d, ysize %d, xoff %d, yoff %d \n", state->xsize,
state->xoff, state->yoff)); state->ysize, state->xoff, state->yoff));
TRACE(("State: bits %d, bytes %d \n", state->bits, state->bytes)); TRACE(("State: bits %d, bytes %d \n", state->bits, state->bytes));
TRACE(("Buffer: %p: %c%c%c%c\n", buffer, (char)buffer[0], (char)buffer[1],(char)buffer[2], (char)buffer[3])); TRACE(("Buffer: %p: %c%c%c%c\n", buffer, (char)buffer[0], (char)buffer[1],
TRACE(("State->Buffer: %c%c%c%c\n", (char)state->buffer[0], (char)state->buffer[1],(char)state->buffer[2], (char)state->buffer[3])); (char)buffer[2], (char)buffer[3]));
TRACE(("Image: mode %s, type %d, bands: %d, xsize %d, ysize %d \n", TRACE(("State->Buffer: %c%c%c%c\n", (char)state->buffer[0],
im->mode, im->type, im->bands, im->xsize, im->ysize)); (char)state->buffer[1], (char)state->buffer[2],
TRACE(("Image: image8 %p, image32 %p, image %p, block %p \n", (char)state->buffer[3]));
im->image8, im->image32, im->image, im->block)); TRACE(("Image: mode %s, type %d, bands: %d, xsize %d, ysize %d \n", im->mode,
TRACE(("Image: pixelsize: %d, linesize %d \n", im->type, im->bands, im->xsize, im->ysize));
im->pixelsize, im->linesize)); TRACE(("Image: image8 %p, image32 %p, image %p, block %p \n", im->image8,
im->image32, im->image, im->block));
TRACE(("Image: pixelsize: %d, linesize %d \n", im->pixelsize, im->linesize));
dump_state(clientstate); dump_state(clientstate);
clientstate->size = bytes; clientstate->size = bytes;
@ -391,54 +415,51 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, Py_
TIFFSetWarningHandlerExt(NULL); TIFFSetWarningHandlerExt(NULL);
if (clientstate->fp) { if (clientstate->fp) {
TRACE(("Opening using fd: %d\n",clientstate->fp)); TRACE(("Opening using fd: %d\n", clientstate->fp));
lseek(clientstate->fp,0,SEEK_SET); // Sometimes, I get it set to the end. lseek(clientstate->fp, 0, SEEK_SET); // Sometimes, I get it set to the end.
tiff = TIFFFdOpen(fd_to_tiff_fd(clientstate->fp), filename, mode); tiff = TIFFFdOpen(fd_to_tiff_fd(clientstate->fp), filename, mode);
} else { } else {
TRACE(("Opening from string\n")); TRACE(("Opening from string\n"));
tiff = TIFFClientOpen(filename, mode, tiff = TIFFClientOpen(filename, mode, (thandle_t)clientstate, _tiffReadProc,
(thandle_t) clientstate, _tiffWriteProc, _tiffSeekProc, _tiffCloseProc,
_tiffReadProc, _tiffWriteProc, _tiffSizeProc, _tiffMapProc, _tiffUnmapProc);
_tiffSeekProc, _tiffCloseProc, _tiffSizeProc,
_tiffMapProc, _tiffUnmapProc);
} }
if (!tiff){ if (!tiff) {
TRACE(("Error, didn't get the tiff\n")); TRACE(("Error, didn't get the tiff\n"));
state->errcode = IMAGING_CODEC_BROKEN; state->errcode = IMAGING_CODEC_BROKEN;
return -1; return -1;
} }
if (clientstate->ifd){ if (clientstate->ifd) {
int rv; int rv;
uint32 ifdoffset = clientstate->ifd; uint32 ifdoffset = clientstate->ifd;
TRACE(("reading tiff ifd %u\n", ifdoffset)); TRACE(("reading tiff ifd %u\n", ifdoffset));
rv = TIFFSetSubDirectory(tiff, ifdoffset); rv = TIFFSetSubDirectory(tiff, ifdoffset);
if (!rv){ if (!rv) {
TRACE(("error in TIFFSetSubDirectory")); TRACE(("error in TIFFSetSubDirectory"));
goto decode_err; goto decode_err;
} }
} }
TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric); TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric);
isYCbCr = photometric == PHOTOMETRIC_YCBCR; isYCbCr = photometric == PHOTOMETRIC_YCBCR;
if (TIFFIsTiled(tiff)) { if (TIFFIsTiled(tiff)) {
INT32 x, y, tile_y; INT32 x, y, tile_y;
UINT32 tile_width, tile_length, current_tile_length, current_line, current_tile_width, row_byte_size; UINT32 tile_width, tile_length, current_tile_length, current_line,
current_tile_width, row_byte_size;
UINT8 *new_data; UINT8 *new_data;
TIFFGetField(tiff, TIFFTAG_TILEWIDTH, &tile_width); TIFFGetField(tiff, TIFFTAG_TILEWIDTH, &tile_width);
TIFFGetField(tiff, TIFFTAG_TILELENGTH, &tile_length); TIFFGetField(tiff, TIFFTAG_TILELENGTH, &tile_length);
/* overflow check for row_byte_size calculation */ /* overflow check for row_byte_size calculation */
if ((UINT32) INT_MAX / state->bits < tile_width) { if ((UINT32)INT_MAX / state->bits < tile_width) {
state->errcode = IMAGING_CODEC_MEMORY; state->errcode = IMAGING_CODEC_MEMORY;
goto decode_err; goto decode_err;
} }
if (isYCbCr) { if (isYCbCr) {
row_byte_size = tile_width * 4; row_byte_size = tile_width * 4;
/* sanity check, we use this value in shuffle below */ /* sanity check, we use this value in shuffle below */
@ -447,7 +468,8 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, Py_
goto decode_err; goto decode_err;
} }
} else { } else {
// We could use TIFFTileSize, but for YCbCr data it returns subsampled data size // We could use TIFFTileSize, but for YCbCr data it returns subsampled
// data size
row_byte_size = (tile_width * state->bits + 7) / 8; row_byte_size = (tile_width * state->bits + 7) / 8;
} }
@ -460,14 +482,15 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, Py_
state->bytes = row_byte_size * tile_length; state->bytes = row_byte_size * tile_length;
if (TIFFTileSize(tiff) > state->bytes) { if (TIFFTileSize(tiff) > state->bytes) {
// If the strip size as expected by LibTiff isn't what we're expecting, abort. // If the strip size as expected by LibTiff isn't what we're expecting,
// abort.
state->errcode = IMAGING_CODEC_MEMORY; state->errcode = IMAGING_CODEC_MEMORY;
goto decode_err; goto decode_err;
} }
/* realloc to fit whole tile */ /* realloc to fit whole tile */
/* malloc check above */ /* malloc check above */
new_data = realloc (state->buffer, state->bytes); new_data = realloc(state->buffer, state->bytes);
if (!new_data) { if (!new_data) {
state->errcode = IMAGING_CODEC_MEMORY; state->errcode = IMAGING_CODEC_MEMORY;
goto decode_err; goto decode_err;
@ -505,14 +528,16 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, Py_
TRACE(("Read tile at %dx%d; \n\n", x, y)); TRACE(("Read tile at %dx%d; \n\n", x, y));
current_tile_width = min((INT32) tile_width, state->xsize - x); current_tile_width = min((INT32)tile_width, state->xsize - x);
current_tile_length = min((INT32) tile_length, state->ysize - y); current_tile_length = min((INT32)tile_length, state->ysize - y);
// iterate over each line in the tile and stuff data into image // iterate over each line in the tile and stuff data into image
for (tile_y = 0; tile_y < current_tile_length; tile_y++) { for (tile_y = 0; tile_y < current_tile_length; tile_y++) {
TRACE(("Writing tile data at %dx%d using tile_width: %d; \n", tile_y + y, x, current_tile_width)); TRACE(("Writing tile data at %dx%d using tile_width: %d; \n",
tile_y + y, x, current_tile_width));
// UINT8 * bbb = state->buffer + tile_y * row_byte_size; // UINT8 * bbb = state->buffer + tile_y * row_byte_size;
// TRACE(("chars: %x%x%x%x\n", ((UINT8 *)bbb)[0], ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3])); // TRACE(("chars: %x%x%x%x\n", ((UINT8 *)bbb)[0], ((UINT8 *)bbb)[1],
// ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3]));
/* /*
* For some reason the TIFFReadRGBATile() function * For some reason the TIFFReadRGBATile() function
* chooses the lower left corner as the origin. * chooses the lower left corner as the origin.
@ -526,23 +551,24 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, Py_
current_line = tile_y; current_line = tile_y;
} }
state->shuffle((UINT8*) im->image[tile_y + y] + x * im->pixelsize, state->shuffle((UINT8 *)im->image[tile_y + y] + x * im->pixelsize,
state->buffer + current_line * row_byte_size, state->buffer + current_line * row_byte_size,
current_tile_width current_tile_width);
);
} }
} }
} }
} else { } else {
if (!isYCbCr) { if (!isYCbCr) {
_decodeStrip(im, state, tiff); ret = _decodeStrip(im, state, tiff);
} else {
ret = _decodeStripYCbCr(im, state, tiff);
} }
else { if (ret == -1) {
_decodeStripYCbCr(im, state, tiff); return ret;
} }
} }
decode_err: decode_err:
TIFFClose(tiff); TIFFClose(tiff);
TRACE(("Done Decoding, Returning \n")); TRACE(("Done Decoding, Returning \n"));
// Returning -1 here to force ImageFile.load to break, rather than // Returning -1 here to force ImageFile.load to break, rather than
@ -559,21 +585,21 @@ int ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int fp) {
// Going to have to deal with the directory as well. // Going to have to deal with the directory as well.
TIFFSTATE *clientstate = (TIFFSTATE *)state->context; TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
int bufsize = 64*1024; int bufsize = 64 * 1024;
char *mode = "w"; char *mode = "w";
TRACE(("initing libtiff\n")); TRACE(("initing libtiff\n"));
TRACE(("Filename %s, filepointer: %d \n", filename, fp)); TRACE(("Filename %s, filepointer: %d \n", filename, fp));
TRACE(("State: count %d, state %d, x %d, y %d, ystep %d\n", state->count, state->state, TRACE(("State: count %d, state %d, x %d, y %d, ystep %d\n", state->count,
state->x, state->y, state->ystep)); state->state, state->x, state->y, state->ystep));
TRACE(("State: xsize %d, ysize %d, xoff %d, yoff %d \n", state->xsize, state->ysize, TRACE(("State: xsize %d, ysize %d, xoff %d, yoff %d \n", state->xsize,
state->xoff, state->yoff)); state->ysize, state->xoff, state->yoff));
TRACE(("State: bits %d, bytes %d \n", state->bits, state->bytes)); TRACE(("State: bits %d, bytes %d \n", state->bits, state->bytes));
TRACE(("State: context %p \n", state->context)); TRACE(("State: context %p \n", state->context));
clientstate->loc = 0; clientstate->loc = 0;
clientstate->size = 0; clientstate->size = 0;
clientstate->eof =0; clientstate->eof = 0;
clientstate->data = 0; clientstate->data = 0;
clientstate->flrealloc = 0; clientstate->flrealloc = 0;
clientstate->fp = fp; clientstate->fp = fp;
@ -581,27 +607,27 @@ int ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int fp) {
state->state = 0; state->state = 0;
if (fp) { if (fp) {
TRACE(("Opening using fd: %d for writing \n",clientstate->fp)); TRACE(("Opening using fd: %d for writing \n", clientstate->fp));
clientstate->tiff = TIFFFdOpen(fd_to_tiff_fd(clientstate->fp), filename, mode); clientstate->tiff =
TIFFFdOpen(fd_to_tiff_fd(clientstate->fp), filename, mode);
} else { } else {
// malloc a buffer to write the tif, we're going to need to realloc or something if we need bigger. // malloc a buffer to write the tif, we're going to need to realloc or
// something if we need bigger.
TRACE(("Opening a buffer for writing \n")); TRACE(("Opening a buffer for writing \n"));
/* malloc check ok, small constant allocation */ /* malloc check ok, small constant allocation */
clientstate->data = malloc(bufsize); clientstate->data = malloc(bufsize);
clientstate->size = bufsize; clientstate->size = bufsize;
clientstate->flrealloc=1; clientstate->flrealloc = 1;
if (!clientstate->data) { if (!clientstate->data) {
TRACE(("Error, couldn't allocate a buffer of size %d\n", bufsize)); TRACE(("Error, couldn't allocate a buffer of size %d\n", bufsize));
return 0; return 0;
} }
clientstate->tiff = TIFFClientOpen(filename, mode, clientstate->tiff = TIFFClientOpen(
(thandle_t) clientstate, filename, mode, (thandle_t)clientstate, _tiffReadProc, _tiffWriteProc,
_tiffReadProc, _tiffWriteProc, _tiffSeekProc, _tiffCloseProc, _tiffSizeProc, _tiffNullMapProc,
_tiffSeekProc, _tiffCloseProc, _tiffSizeProc, _tiffUnmapProc); /*force no mmap*/
_tiffNullMapProc, _tiffUnmapProc); /*force no mmap*/
} }
if (!clientstate->tiff) { if (!clientstate->tiff) {
@ -610,11 +636,13 @@ int ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int fp) {
} }
return 1; return 1;
} }
int ImagingLibTiffMergeFieldInfo(ImagingCodecState state, TIFFDataType field_type, int key, int is_var_length){ int ImagingLibTiffMergeFieldInfo(ImagingCodecState state,
// Refer to libtiff docs (http://www.simplesystems.org/libtiff/addingtags.html) TIFFDataType field_type, int key,
int is_var_length) {
// Refer to libtiff docs
// (http://www.simplesystems.org/libtiff/addingtags.html)
TIFFSTATE *clientstate = (TIFFSTATE *)state->context; TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
uint32 n; uint32 n;
int status = 0; int status = 0;
@ -627,9 +655,8 @@ int ImagingLibTiffMergeFieldInfo(ImagingCodecState state, TIFFDataType field_typ
// whether the first value should encode the number of values. // whether the first value should encode the number of values.
int passcount = 0; int passcount = 0;
TIFFFieldInfo info[] = { TIFFFieldInfo info[] = {{key, readcount, writecount, field_type, FIELD_CUSTOM,
{ key, readcount, writecount, field_type, FIELD_CUSTOM, 1, passcount, "CustomField" } 1, passcount, "CustomField"}};
};
if (is_var_length) { if (is_var_length) {
info[0].field_writecount = -1; info[0].field_writecount = -1;
@ -642,7 +669,8 @@ int ImagingLibTiffMergeFieldInfo(ImagingCodecState state, TIFFDataType field_typ
n = sizeof(info) / sizeof(info[0]); n = sizeof(info) / sizeof(info[0]);
// Test for libtiff 4.0 or later, excluding libtiff 3.9.6 and 3.9.7 // Test for libtiff 4.0 or later, excluding libtiff 3.9.6 and 3.9.7
#if TIFFLIB_VERSION >= 20111221 && TIFFLIB_VERSION != 20120218 && TIFFLIB_VERSION != 20120922 #if TIFFLIB_VERSION >= 20111221 && TIFFLIB_VERSION != 20120218 && \
TIFFLIB_VERSION != 20120922
status = TIFFMergeFieldInfo(clientstate->tiff, info, n); status = TIFFMergeFieldInfo(clientstate->tiff, info, n);
#else #else
TIFFMergeFieldInfo(clientstate->tiff, info, n); TIFFMergeFieldInfo(clientstate->tiff, info, n);
@ -650,7 +678,7 @@ int ImagingLibTiffMergeFieldInfo(ImagingCodecState state, TIFFDataType field_typ
return status; return status;
} }
int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...){ int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...) {
// after tif_dir.c->TIFFSetField. // after tif_dir.c->TIFFSetField.
TIFFSTATE *clientstate = (TIFFSTATE *)state->context; TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
va_list ap; va_list ap;
@ -662,14 +690,14 @@ int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...){
return status; return status;
} }
int ImagingLibTiffEncode(Imaging im, ImagingCodecState state, UINT8 *buffer,
int ImagingLibTiffEncode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes) { int bytes) {
/* One shot encoder. Encode everything to the tiff in the clientstate. /* One shot encoder. Encode everything to the tiff in the clientstate.
If we're running off of a FD, then run once, we're good, everything If we're running off of a FD, then run once, we're good, everything
ends up in the file, we close and we're done. ends up in the file, we close and we're done.
If we're going to memory, then we need to write the whole file into memory, then If we're going to memory, then we need to write the whole file into memory,
parcel it back out to the pystring buffer bytes at a time. then parcel it back out to the pystring buffer bytes at a time.
*/ */
@ -677,35 +705,38 @@ int ImagingLibTiffEncode(Imaging im, ImagingCodecState state, UINT8* buffer, int
TIFF *tiff = clientstate->tiff; TIFF *tiff = clientstate->tiff;
TRACE(("in encoder: bytes %d\n", bytes)); TRACE(("in encoder: bytes %d\n", bytes));
TRACE(("State: count %d, state %d, x %d, y %d, ystep %d\n", state->count, state->state, TRACE(("State: count %d, state %d, x %d, y %d, ystep %d\n", state->count,
state->x, state->y, state->ystep)); state->state, state->x, state->y, state->ystep));
TRACE(("State: xsize %d, ysize %d, xoff %d, yoff %d \n", state->xsize, state->ysize, TRACE(("State: xsize %d, ysize %d, xoff %d, yoff %d \n", state->xsize,
state->xoff, state->yoff)); state->ysize, state->xoff, state->yoff));
TRACE(("State: bits %d, bytes %d \n", state->bits, state->bytes)); TRACE(("State: bits %d, bytes %d \n", state->bits, state->bytes));
TRACE(("Buffer: %p: %c%c%c%c\n", buffer, (char)buffer[0], (char)buffer[1],(char)buffer[2], (char)buffer[3])); TRACE(("Buffer: %p: %c%c%c%c\n", buffer, (char)buffer[0], (char)buffer[1],
TRACE(("State->Buffer: %c%c%c%c\n", (char)state->buffer[0], (char)state->buffer[1],(char)state->buffer[2], (char)state->buffer[3])); (char)buffer[2], (char)buffer[3]));
TRACE(("Image: mode %s, type %d, bands: %d, xsize %d, ysize %d \n", TRACE(("State->Buffer: %c%c%c%c\n", (char)state->buffer[0],
im->mode, im->type, im->bands, im->xsize, im->ysize)); (char)state->buffer[1], (char)state->buffer[2],
TRACE(("Image: image8 %p, image32 %p, image %p, block %p \n", (char)state->buffer[3]));
im->image8, im->image32, im->image, im->block)); TRACE(("Image: mode %s, type %d, bands: %d, xsize %d, ysize %d \n", im->mode,
TRACE(("Image: pixelsize: %d, linesize %d \n", im->type, im->bands, im->xsize, im->ysize));
im->pixelsize, im->linesize)); TRACE(("Image: image8 %p, image32 %p, image %p, block %p \n", im->image8,
im->image32, im->image, im->block));
TRACE(("Image: pixelsize: %d, linesize %d \n", im->pixelsize, im->linesize));
dump_state(clientstate); dump_state(clientstate);
if (state->state == 0) { if (state->state == 0) {
TRACE(("Encoding line bt line")); TRACE(("Encoding line bt line"));
while(state->y < state->ysize){ while (state->y < state->ysize) {
state->shuffle(state->buffer, state->shuffle(state->buffer,
(UINT8*) im->image[state->y + state->yoff] + (UINT8 *)im->image[state->y + state->yoff] +
state->xoff * im->pixelsize, state->xoff * im->pixelsize,
state->xsize); state->xsize);
if (TIFFWriteScanline(tiff, (tdata_t)(state->buffer), (uint32)state->y, 0) == -1) { if (TIFFWriteScanline(tiff, (tdata_t)(state->buffer), (uint32)state->y,
0) == -1) {
TRACE(("Encode Error, row %d\n", state->y)); TRACE(("Encode Error, row %d\n", state->y));
state->errcode = IMAGING_CODEC_BROKEN; state->errcode = IMAGING_CODEC_BROKEN;
TIFFClose(tiff); TIFFClose(tiff);
if (!clientstate->fp){ if (!clientstate->fp) {
free(clientstate->data); free(clientstate->data);
} }
return -1; return -1;
@ -714,7 +745,7 @@ int ImagingLibTiffEncode(Imaging im, ImagingCodecState state, UINT8* buffer, int
} }
if (state->y == state->ysize) { if (state->y == state->ysize) {
state->state=1; state->state = 1;
TRACE(("Flushing \n")); TRACE(("Flushing \n"));
if (!TIFFFlush(tiff)) { if (!TIFFFlush(tiff)) {
@ -722,7 +753,7 @@ int ImagingLibTiffEncode(Imaging im, ImagingCodecState state, UINT8* buffer, int
// likely reason is memory. // likely reason is memory.
state->errcode = IMAGING_CODEC_MEMORY; state->errcode = IMAGING_CODEC_MEMORY;
TIFFClose(tiff); TIFFClose(tiff);
if (!clientstate->fp){ if (!clientstate->fp) {
free(clientstate->data); free(clientstate->data);
} }
return -1; return -1;
@ -737,7 +768,8 @@ int ImagingLibTiffEncode(Imaging im, ImagingCodecState state, UINT8* buffer, int
if (state->state == 1 && !clientstate->fp) { if (state->state == 1 && !clientstate->fp) {
int read = (int)_tiffReadProc(clientstate, (tdata_t)buffer, (tsize_t)bytes); int read = (int)_tiffReadProc(clientstate, (tdata_t)buffer, (tsize_t)bytes);
TRACE(("Buffer: %p: %c%c%c%c\n", buffer, (char)buffer[0], (char)buffer[1],(char)buffer[2], (char)buffer[3])); TRACE(("Buffer: %p: %c%c%c%c\n", buffer, (char)buffer[0], (char)buffer[1],
(char)buffer[2], (char)buffer[3]));
if (clientstate->loc == clientstate->eof) { if (clientstate->loc == clientstate->eof) {
TRACE(("Hit EOF, calling an end, freeing data")); TRACE(("Hit EOF, calling an end, freeing data"));
state->errcode = IMAGING_CODEC_END; state->errcode = IMAGING_CODEC_END;
@ -750,10 +782,6 @@ int ImagingLibTiffEncode(Imaging im, ImagingCodecState state, UINT8* buffer, int
return 0; return 0;
} }
const char* const char *ImagingTiffVersion(void) { return TIFFGetVersion(); }
ImagingTiffVersion(void)
{
return TIFFGetVersion();
}
#endif #endif