From 1d9632ecfb70c756f24cbc19ccd88673c35a4e23 Mon Sep 17 00:00:00 2001 From: Lazizbek Ergashev Date: Tue, 25 Aug 2026 13:05:00 +0500 Subject: [PATCH 1/3] Reject NaN and out-of-range blur radii --- Tests/test_image_filter.py | 9 +++++++++ src/libImaging/BoxBlur.c | 5 ++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Tests/test_image_filter.py b/Tests/test_image_filter.py index 0a4abdae1b6..f689fa4a4b5 100644 --- a/Tests/test_image_filter.py +++ b/Tests/test_image_filter.py @@ -272,6 +272,15 @@ def test_box_blur_non_finite_radius(radius: float | tuple[float, float]) -> None ImageFilter.BoxBlur(radius) +@pytest.mark.parametrize("radius", (float("nan"), float("inf"), 2**31)) +def test_out_of_range_blur_filter_radius(radius: float) -> None: + im = hopper() + with pytest.raises(ValueError): + im.filter(ImageFilter.BoxBlur(radius)) + with pytest.raises(ValueError): + im.filter(ImageFilter.GaussianBlur(radius)) + + def test_rankfilter_size_1() -> None: im = Image.new("L", (3, 3), 128) diff --git a/src/libImaging/BoxBlur.c b/src/libImaging/BoxBlur.c index 58eaa075877..3c5fe5d6573 100644 --- a/src/libImaging/BoxBlur.c +++ b/src/libImaging/BoxBlur.c @@ -241,9 +241,12 @@ ImagingBoxBlur(Imaging imOut, Imaging imIn, float xradius, float yradius, int n) if (n < 1) { return ImagingError_ValueError("number of passes must be greater than zero"); } - if (xradius < 0 || yradius < 0) { + if (!(xradius >= 0) || !(yradius >= 0)) { return ImagingError_ValueError("radius must be >= 0"); } + if (xradius >= INT_MAX || yradius >= INT_MAX) { + return ImagingError_ValueError("radius is too large"); + } if (imIn->mode != imOut->mode || imIn->type != imOut->type || imIn->bands != imOut->bands || imIn->xsize != imOut->xsize || From 797d958a2f8160d2da5557b17279b5379e3e3af7 Mon Sep 17 00:00:00 2001 From: Lazizbek Ergashev Date: Tue, 25 Aug 2026 13:13:37 +0500 Subject: [PATCH 2/3] Avoid comparing a float radius against INT_MAX --- Tests/test_image_filter.py | 4 ++-- src/libImaging/BoxBlur.c | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Tests/test_image_filter.py b/Tests/test_image_filter.py index f689fa4a4b5..5276ec8712c 100644 --- a/Tests/test_image_filter.py +++ b/Tests/test_image_filter.py @@ -275,9 +275,9 @@ def test_box_blur_non_finite_radius(radius: float | tuple[float, float]) -> None @pytest.mark.parametrize("radius", (float("nan"), float("inf"), 2**31)) def test_out_of_range_blur_filter_radius(radius: float) -> None: im = hopper() - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="radius"): im.filter(ImageFilter.BoxBlur(radius)) - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="radius"): im.filter(ImageFilter.GaussianBlur(radius)) diff --git a/src/libImaging/BoxBlur.c b/src/libImaging/BoxBlur.c index 3c5fe5d6573..b1a8fd04a19 100644 --- a/src/libImaging/BoxBlur.c +++ b/src/libImaging/BoxBlur.c @@ -241,11 +241,13 @@ ImagingBoxBlur(Imaging imOut, Imaging imIn, float xradius, float yradius, int n) if (n < 1) { return ImagingError_ValueError("number of passes must be greater than zero"); } + /* Negated comparisons, so that NaN is rejected as well. */ if (!(xradius >= 0) || !(yradius >= 0)) { return ImagingError_ValueError("radius must be >= 0"); } - if (xradius >= INT_MAX || yradius >= INT_MAX) { - return ImagingError_ValueError("radius is too large"); + /* 2**31 and above cannot be converted to an int. */ + if (xradius >= 2147483648.0f || yradius >= 2147483648.0f) { + return ImagingError_ValueError("radius too large"); } if (imIn->mode != imOut->mode || imIn->type != imOut->type || From 97d7caaa4a1bc73f7cf7b5d897a446c305eff84c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 11 Sep 2026 10:18:08 +1000 Subject: [PATCH 3/3] Use int max in Python --- Tests/test_image_filter.py | 49 ++++++++++---------------------------- src/PIL/ImageFilter.py | 8 ++++--- 2 files changed, 17 insertions(+), 40 deletions(-) diff --git a/Tests/test_image_filter.py b/Tests/test_image_filter.py index 5276ec8712c..974d4b7a3ad 100644 --- a/Tests/test_image_filter.py +++ b/Tests/test_image_filter.py @@ -236,47 +236,22 @@ def test_consistency_i16_high_byte(mode: str) -> None: assert im.getpixel((4, 4)) == 1000 -@pytest.mark.parametrize( - "radius", - ( - -2, - (-2, -2), - (-2, 2), - (2, -2), - ), -) -def test_invalid_box_blur_filter(radius: int | tuple[int, int]) -> None: - with pytest.raises(ValueError): - ImageFilter.BoxBlur(radius) - - im = hopper() - box_blur_filter = ImageFilter.BoxBlur(2) - box_blur_filter.radius = radius - with pytest.raises(ValueError): - im.filter(box_blur_filter) +@pytest.mark.parametrize("size", (-1, math.nan, math.inf, 2**31)) +def test_invalid_box_blur_filter(size: int) -> None: + for radius in (size, (size, size), (size, 1), (1, size)): + with pytest.raises(ValueError, match="radius"): + ImageFilter.BoxBlur(radius) - -@pytest.mark.parametrize( - "radius", - ( - math.nan, - (math.nan, 1), - (1, math.nan), - math.inf, - (1, math.inf), - (math.inf, 1), - ), -) -def test_box_blur_non_finite_radius(radius: float | tuple[float, float]) -> None: - with pytest.raises(ValueError, match="radius must be a finite number >= 0"): - ImageFilter.BoxBlur(radius) + im = hopper() + box_blur_filter = ImageFilter.BoxBlur(2) + box_blur_filter.radius = radius + with pytest.raises(ValueError, match="radius"): + im.filter(box_blur_filter) -@pytest.mark.parametrize("radius", (float("nan"), float("inf"), 2**31)) -def test_out_of_range_blur_filter_radius(radius: float) -> None: +@pytest.mark.parametrize("radius", (math.nan, math.inf, 2**31)) +def test_invalid_gaussian_blur_filter(radius: int) -> None: im = hopper() - with pytest.raises(ValueError, match="radius"): - im.filter(ImageFilter.BoxBlur(radius)) with pytest.raises(ValueError, match="radius"): im.filter(ImageFilter.GaussianBlur(radius)) diff --git a/src/PIL/ImageFilter.py b/src/PIL/ImageFilter.py index 5d6a1d5ec6c..9fc66a232fa 100644 --- a/src/PIL/ImageFilter.py +++ b/src/PIL/ImageFilter.py @@ -17,7 +17,6 @@ from __future__ import annotations import abc -import math from typing import cast TYPE_CHECKING = False @@ -221,8 +220,11 @@ class BoxBlur(MultibandFilter): def __init__(self, radius: float | Sequence[float]) -> None: xy = radius if isinstance(radius, (tuple, list)) else (radius, radius) - if not all(math.isfinite(value) and value >= 0 for value in xy): - msg = "radius must be a finite number >= 0" + if not all(value >= 0 for value in xy): + msg = "radius must be >= 0" + raise ValueError(msg) + if any(value >= 2**31 for value in xy): + msg = "radius too large" raise ValueError(msg) self.radius = radius