From 6a8de891fb00968e5ea79bfa84368ed90b3cfc1d Mon Sep 17 00:00:00 2001
From: Andrew Murray <3112309+radarhere@users.noreply.github.com>
Date: Fri, 26 Jun 2026 07:35:42 +1000
Subject: [PATCH] Ensure map stride is at least one full row of pixels (#9719)

Co-authored-by: GameZoneHacker <devanshshah2003@hotmail.com>
---
 Tests/test_file_mcidas.py | 25 +++++++++++++++++++++++++
 src/map.c                 | 18 ++++++++++--------
 2 files changed, 35 insertions(+), 8 deletions(-)

Index: pillow-11.3.0/Tests/test_file_mcidas.py
===================================================================
--- pillow-11.3.0.orig/Tests/test_file_mcidas.py
+++ pillow-11.3.0/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
Index: pillow-11.3.0/src/map.c
===================================================================
--- pillow-11.3.0.orig/src/map.c
+++ pillow-11.3.0/src/map.c
@@ -82,14 +82,16 @@ PyImaging_MapBuffer(PyObject *self, PyOb
         return NULL;
     }
 
-    if (stride <= 0) {
-        if (!strcmp(mode, "L") || !strcmp(mode, "P")) {
-            stride = xsize;
-        } else if (!strncmp(mode, "I;16", 4)) {
-            stride = xsize * 2;
-        } else {
-            stride = xsize * 4;
-        }
+    int pixelsize;
+    if (!strcmp(mode, "L") || !strcmp(mode, "P")) {
+        pixelsize = 1;
+    } else if (!strncmp(mode, "I;16", 4)) {
+        pixelsize = 2;
+    } else {
+        pixelsize = 4;
+    }
+    if (stride <= xsize * pixelsize) {
+        stride = xsize * pixelsize;
     }
 
     if (stride > 0 && ysize > PY_SSIZE_T_MAX / stride) {
