diff --git a/Tests/test_image_filter.py b/Tests/test_image_filter.py index 4fa24e2f9c5..e0124dd6f55 100644 --- a/Tests/test_image_filter.py +++ b/Tests/test_image_filter.py @@ -235,6 +235,25 @@ def test_invalid_box_blur_filter(radius: int | tuple[int, int]) -> None: 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: + im = hopper() + with pytest.raises(ValueError, match="radius"): + im.filter(ImageFilter.BoxBlur(radius)) + with pytest.raises(ValueError, match="radius"): + im.filter(ImageFilter.GaussianBlur(radius)) + + +@pytest.mark.parametrize("radius", (2**24, 2**30)) +def test_large_blur_filter_radius(radius: int) -> None: + # A radius this large overflows the accumulators unless they stay unsigned + im = Image.new("L", (3, 3), 128) + assert im.filter(ImageFilter.BoxBlur(radius)).getpixel((1, 1)) == 128 + + im = Image.new("RGB", (3, 3), (128, 128, 128)) + assert im.filter(ImageFilter.BoxBlur(radius)).getpixel((1, 1)) == (128, 128, 128) + + 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 4fea4fe44b8..1d70c6ee8c3 100644 --- a/src/libImaging/BoxBlur.c +++ b/src/libImaging/BoxBlur.c @@ -40,10 +40,10 @@ void static inline ImagingLineBoxBlur32( /* Compute acc for -1 pixel (outside of image): From "-radius-1" to "-1" get first pixel, then from "0" to "radius-1". */ - acc[0] = lineIn[0][0] * (radius + 1); - acc[1] = lineIn[0][1] * (radius + 1); - acc[2] = lineIn[0][2] * (radius + 1); - acc[3] = lineIn[0][3] * (radius + 1); + acc[0] = lineIn[0][0] * (UINT32)(radius + 1); + acc[1] = lineIn[0][1] * (UINT32)(radius + 1); + acc[2] = lineIn[0][2] * (UINT32)(radius + 1); + acc[3] = lineIn[0][3] * (UINT32)(radius + 1); /* As radius can be bigger than xsize, iterate to edgeA -1. */ for (x = 0; x < edgeA - 1; x++) { acc[0] += lineIn[x][0]; @@ -52,10 +52,10 @@ void static inline ImagingLineBoxBlur32( acc[3] += lineIn[x][3]; } /* Then multiply remainder to last x. */ - acc[0] += lineIn[lastx][0] * (radius - edgeA + 1); - acc[1] += lineIn[lastx][1] * (radius - edgeA + 1); - acc[2] += lineIn[lastx][2] * (radius - edgeA + 1); - acc[3] += lineIn[lastx][3] * (radius - edgeA + 1); + acc[0] += lineIn[lastx][0] * (UINT32)(radius - edgeA + 1); + acc[1] += lineIn[lastx][1] * (UINT32)(radius - edgeA + 1); + acc[2] += lineIn[lastx][2] * (UINT32)(radius - edgeA + 1); + acc[3] += lineIn[lastx][3] * (UINT32)(radius - edgeA + 1); if (edgeA <= edgeB) { /* Subtract pixel from left ("0"). @@ -123,11 +123,11 @@ void static inline ImagingLineBoxBlur8( #define SAVE(x, bulk) lineOut[x] = (UINT8)((bulk + (1 << 23)) >> 24) - acc = lineIn[0] * (radius + 1); + acc = lineIn[0] * (UINT32)(radius + 1); for (x = 0; x < edgeA - 1; x++) { acc += lineIn[x]; } - acc += lineIn[lastx] * (radius - edgeA + 1); + acc += lineIn[lastx] * (UINT32)(radius - edgeA + 1); if (edgeA <= edgeB) { for (x = 0; x < edgeA; x++) { @@ -176,7 +176,7 @@ ImagingHorizontalBoxBlur(Imaging imOut, Imaging imIn, float floatRadius) { int radius = (int)floatRadius; UINT32 ww = (UINT32)(1 << 24) / (floatRadius * 2 + 1); - UINT32 fw = ((1 << 24) - (radius * 2 + 1) * ww) / 2; + UINT32 fw = ((1 << 24) - ((UINT32)radius * 2 + 1) * ww) / 2; int edgeA = MIN(radius + 1, imIn->xsize); int edgeB = MAX(imIn->xsize - radius - 1, 0); @@ -244,9 +244,14 @@ 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) { + /* Negated comparisons, so that NaN is rejected as well. */ + if (!(xradius >= 0) || !(yradius >= 0)) { return ImagingError_ValueError("radius must be >= 0"); } + /* 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 || imIn->bands != imOut->bands || imIn->xsize != imOut->xsize ||