diff --git a/src/read.c b/src/read.c index b2ef7d9..93bba62 100644 --- a/src/read.c +++ b/src/read.c @@ -198,8 +198,10 @@ SEXP read_tiff(SEXP sFn, SEXP sNative, SEXP sAll, SEXP sConvert, SEXP sInfo, SEX } tiff = TIFF_Open("rmc", &rj); /* no mmap, no chopping */ - if (!tiff) + if (!tiff) { + if (f) fclose(f); Rf_error("Unable to open TIFF"); + } int cur_dir = 0; /* 1-based image number */ int nprot = 0; @@ -539,11 +541,15 @@ SEXP read_tiff(SEXP sFn, SEXP sNative, SEXP sAll, SEXP sConvert, SEXP sInfo, SEX } } } else { /* tiled image */ - if (indexed || colormap[0] || bps == 12) + if (indexed || colormap[0] || bps == 12) { + TIFFClose(tiff); Rf_error("Indexed and 12-bit tiled images are not supported."); + } - if (spp > 1 && config != PLANARCONFIG_CONTIG) + if (spp > 1 && config != PLANARCONFIG_CONTIG) { + TIFFClose(tiff); Rf_error("Planar format tiled images are not supported"); + } #ifdef TIFF_DEBUG Rprintf(" - %d x %d tiles\n", TIFFNumberOfTiles(tiff), TIFFTileSize(tiff)); diff --git a/src/write.c b/src/write.c index 60cc985..3488513 100644 --- a/src/write.c +++ b/src/write.c @@ -26,6 +26,24 @@ int analyze_native(const unsigned int *what, int length) { return alpha | (ac << 1); } +/* Close the TIFF before raising an R error. + + Rf_error() performs a longjmp, so an error raised after TIFF_Open() would + otherwise return to R with the TIFF still open. That matters because the + TIFF's client data is the stack-allocated tiff_job_t below, and because + common.c keeps the most recently opened TIFF in the static `last_tiff`. + The next libtiff error - e.g. a subsequent readTIFF() on a file with an + unreadable header - makes TIFFErrorHandler_() call TIFFClose(last_tiff) + on that stale TIFF, dereferencing a stack frame that no longer exists. + + TIFFClose() also runs TIFFCloseProc_(), which fcloses the output file (or + frees the in-memory buffer) and resets last_tiff, so this is the complete + cleanup for both output targets. */ +static void tiff_close_and_error(TIFF *tiff, const char *msg) { + TIFFClose(tiff); + Rf_error("%s", msg); +} + SEXP write_tiff(SEXP image, SEXP where, SEXP sBPS, SEXP sCompr, SEXP sReduce) { SEXP dims, img_list = 0; tiff_job_t rj; @@ -81,11 +99,11 @@ SEXP write_tiff(SEXP image, SEXP where, SEXP sBPS, SEXP sCompr, SEXP sReduce) { raw_array = 1; if (!native && !raw_array && TYPEOF(image) != REALSXP) - Rf_error("image must be a matrix or array of raw or real numbers"); + tiff_close_and_error(tiff, "image must be a matrix or array of raw or real numbers"); dims = Rf_getAttrib(image, R_DimSymbol); if (dims == R_NilValue || TYPEOF(dims) != INTSXP || LENGTH(dims) < 2 || LENGTH(dims) > 3) - Rf_error("image must be a matrix or an array of two or three dimensions"); + tiff_close_and_error(tiff, "image must be a matrix or an array of two or three dimensions"); if (raw_array && LENGTH(dims) == 3) { /* raw arrays have either bpp, width, height or width, height dimensions */ planes = INTEGER(dims)[0]; @@ -99,7 +117,7 @@ SEXP write_tiff(SEXP image, SEXP where, SEXP sBPS, SEXP sCompr, SEXP sReduce) { } if (planes < 1 || planes > 4) - Rf_error("image must have either 1 (grayscale), 2 (GA), 3 (RGB) or 4 (RGBA) planes"); + tiff_close_and_error(tiff, "image must have either 1 (grayscale), 2 (GA), 3 (RGB) or 4 (RGBA) planes"); if (native) { /* nativeRaster should have a "channels" attribute if it has anything else than 4 channels */ SEXP cha = getAttrib(image, install("channels")); @@ -112,7 +130,7 @@ SEXP write_tiff(SEXP image, SEXP where, SEXP sBPS, SEXP sCompr, SEXP sReduce) { } if (raw_array) { if (planes != 4) - Rf_error("Only RGBA format is supported as raw data"); + tiff_close_and_error(tiff, "Only RGBA format is supported as raw data"); native = 1; /* from now on we treat raw arrays like native */ } @@ -200,7 +218,7 @@ SEXP write_tiff(SEXP image, SEXP where, SEXP sBPS, SEXP sCompr, SEXP sReduce) { data16 = (unsigned short*) buf; data32 = (unsigned int*) buf; if (!buf) - Rf_error("cannot allocate output image buffer"); + tiff_close_and_error(tiff, "cannot allocate output image buffer"); if (bps == 8) for (y = 0; y < height; y++) for (x = 0; x < width; x++)