diff --git a/Tests/test_file_mcidas.py b/Tests/test_file_mcidas.py index 7e236028d18..d1410f5036c 100644 --- a/Tests/test_file_mcidas.py +++ b/Tests/test_file_mcidas.py @@ -1,5 +1,8 @@ from __future__ import annotations +import struct +from pathlib import Path + import pytest from PIL import Image, McIdasImagePlugin @@ -14,6 +17,28 @@ def test_invalid_file() -> None: McIdasImagePlugin.McIdasImageFile(invalid_file) +def test_undersized_stride(tmp_path: Path) -> None: + # A crafted area descriptor declares a row stride far smaller than a full + # row of pixels. Memory mapping must not lay out row pointers at that + # stride, which would read past the mapped buffer; the image is rejected + # instead of leaking memory or crashing. + words = [0] * 65 + words[2] = 4 # magic: 00 00 00 00 00 00 00 04 + words[9] = 1 # ysize + words[10] = 200000 # xsize -> a full row is 200000 bytes (mode "L") + words[11] = 1 # mode "L" + words[14] = 0 # zeroes the xsize term of the stride + words[15] = 1 # stride = 1 (much smaller than a row) + data = struct.pack("!64i", *words[1:65]) + + path = tmp_path / "undersized_stride.area" + path.write_bytes(data) + + with Image.open(path) as im: + with pytest.raises(ValueError, match="buffer is not large enough"): + im.load() + + def test_valid_file() -> None: # Arrange # https://ghrc.nsstc.nasa.gov/hydro/details/cmx3g8 diff --git a/src/map.c b/src/map.c index 6f66b0cc57b..4c9bb2ae34c 100644 --- a/src/map.c +++ b/src/map.c @@ -84,14 +84,16 @@ PyImaging_MapBuffer(PyObject *self, PyObject *args) { const ModeID mode = findModeID(mode_name); - if (stride <= 0) { - if (mode == IMAGING_MODE_L || mode == IMAGING_MODE_P) { - stride = xsize; - } else if (isModeI16(mode)) { - stride = xsize * 2; - } else { - stride = xsize * 4; - } + int pixelsize; + if (mode == IMAGING_MODE_L || mode == IMAGING_MODE_P) { + pixelsize = 1; + } else if (isModeI16(mode)) { + pixelsize = 2; + } else { + pixelsize = 4; + } + if (stride <= xsize * pixelsize) { + stride = xsize * pixelsize; } if (stride > 0 && ysize > PY_SSIZE_T_MAX / stride) {