diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index 88b0191c82d..ffb5b1de113 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -255,6 +255,29 @@ def test_oversized_box_length(tbox: bytes) -> None: Jpeg2KImagePlugin.Jpeg2KImageFile(BytesIO(data)) +def test_large_skipped_box() -> None: + # A box larger than the stream buffer reaches the skip callback + xml = b" " * 2 * 1024 * 1024 + box = _binary.o32be(8 + len(xml)) + b"xml " + xml + with open("Tests/images/test-card-lossless.jp2", "rb") as fp: + data = fp.read() + data = data[:326] + box + data[326:] + + with Image.open(BytesIO(data)) as im: + assert_image_equal_tofile(im, "Tests/images/test-card-lossless.jp2") + + class FailingSeek(BytesIO): + def seek(self, offset: int, whence: int = os.SEEK_SET) -> int: + if whence == os.SEEK_CUR and offset > 512 * 1024: + msg = "seek refused" + raise OSError(msg) + return super().seek(offset, whence) + + with Image.open(FailingSeek(data)) as im: + with pytest.raises(OSError, match="seek refused"): + im.load() + + def test_layers_type(card: ImageFile.ImageFile, tmp_path: Path) -> None: outfile = tmp_path / "temp_layers.jp2" for quality_layers in [[100, 50, 10], (100, 50, 10), None]: diff --git a/src/decode.c b/src/decode.c index 4f96fd36491..e78b7a22984 100644 --- a/src/decode.c +++ b/src/decode.c @@ -140,6 +140,9 @@ _decode(ImagingDecoderObject *decoder, PyObject *args) { } PyBuffer_Release(&buffer); + if (PyErr_Occurred()) { + return NULL; + } return Py_BuildValue("ii", status, decoder->state.errcode); } diff --git a/src/libImaging/Jpeg2KDecode.c b/src/libImaging/Jpeg2KDecode.c index fb0e1fc6a00..e069a0b2ce5 100644 --- a/src/libImaging/Jpeg2KDecode.c +++ b/src/libImaging/Jpeg2KDecode.c @@ -53,13 +53,13 @@ j2k_read(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) { static OPJ_OFF_T j2k_skip(OPJ_OFF_T p_nb_bytes, void *p_user_data) { - off_t pos; ImagingCodecState state = (ImagingCodecState)p_user_data; - _imaging_seek_pyFd(state->fd, p_nb_bytes, SEEK_CUR); - pos = _imaging_tell_pyFd(state->fd); + if (_imaging_seek_pyFd(state->fd, p_nb_bytes, SEEK_CUR) == -1) { + return (OPJ_OFF_T)-1; + } - return pos ? pos : (OPJ_OFF_T)-1; + return p_nb_bytes; } /* -------------------------------------------------------------------- */