python3-pillow: fix CVE-2026-54058

Pillow prior to 12.3.0, when loading an uncompressed McIdas AREA image
through the mmap raw codec path, allows an attacker-controlled row stride
smaller than the natural row width, causing pixel access to read beyond the
mapped region and disclose adjacent process memory or fault.

Ensure the map stride is at least one full row of pixels.

Backport the patch to fix CVE-2026-54058
https://github.com/python-pillow/Pillow/commit/6a8de891fb00968e5ea79bfa84368ed90b3cfc1d

Reference:
https://nvd.nist.gov/vuln/detail/CVE-2026-54058

Signed-off-by: Aravind Bandari <aravind.bandari@windriver.com>
Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
This commit is contained in:
Aravind Bandari authored and Anuj Mittal committed 2026-10-05 14:40:50 +05:30
1 parent b48b8d52fe
commit de904fbb90
2 files changed
+90

No files matched your search

@@ -0,0 +1,89 @@
From 04410f2e38c070e6d519c24d1cf315612b8adb93 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)
CVE: CVE-2026-54058
Upstream-Status: Backport [https://github.com/python-pillow/Pillow/commit/6a8de891fb00968e5ea79bfa84368ed90b3cfc1d]
Signed-off-by: Aravind Bandari <aravind.bandari@windriver.com>
Co-authored-by: GameZoneHacker <devanshshah2003@hotmail.com>
---
Tests/test_file_mcidas.py | 25 +++++++++++++++++++++++++
src/map.c | 18 ++++++++++--------
2 files changed, 35 insertions(+), 8 deletions(-)
diff --git a/Tests/test_file_mcidas.py b/Tests/test_file_mcidas.py
index 7e236028d..d1410f503 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 6f66b0cc5..4c9bb2ae3 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) {
--
2.53.0
@@ -7,6 +7,7 @@ LIC_FILES_CHKSUM = "file://LICENSE;md5=a6f0ac3777cfc96ded1b825e32ae7c99"
SRC_URI = "git://github.com/python-pillow/Pillow.git;branch=main;protocol=https;tag=${PV} \
file://0001-support-cross-compiling.patch \
file://CVE-2026-54058.patch \
"
SRCREV = "3c41c095064200a02672d89cc5ff629eaf4b0d4f"