Skip to content

Fix integer overflow in 16-bit resampling #9480

Open
hayatoikoma wants to merge 11 commits intopython-pillow:mainfrom
hayatoikoma:fixresample
Open

Fix integer overflow in 16-bit resampling #9480
hayatoikoma wants to merge 11 commits intopython-pillow:mainfrom
hayatoikoma:fixresample

Conversation

@hayatoikoma
Copy link
Copy Markdown

📝 Description
This PR fixes a bug in Resample.c where downsampling 16-bit images (I;16) using filters with negative lobes (such as Image.Resampling.LANCZOS) could result in byte corruption.

Because Lanczos weighting can create overshoots (ringing artifacts) near sharp edges, the accumulated floating-point sum can sometimes exceed the 16-bit maximum (65535) or fall below zero. Previously, these out-of-bounds values were not correctly clamped before being cast or packed into the 16-bit output buffer, leading to integer overflow/underflow and corrupted pixels.

This update correctly clamps the accumulated float values to the [0, 65535] range for I;16 images during resampling.

🛠️ Changes Made

  • src/libImaging/Resample.c: Added bounding/clamping logic to ensure the accumulated float sum is strictly clamped to 0 and 65535 before writing to the I;16 output buffer.
  • Unit Tests: Added test_resampling_clamp to verify this behavior. The test constructs an image with a hard step edge (0 to 65535) and applies a 5x Lanczos downsampling, comparing the clamped I;16 output against a float (F) reference image to ensure no byte corruption occurs.

@hayatoikoma hayatoikoma changed the title Fix integer overflow/byte corruption in 16-bit (I;16) resampling by clamping float sums Fix integer overflow in 16-bit resampling Mar 20, 2026
This commit fixes a bug in Resample.c where downsampling 16-bit
images (I;16) using filters with negative lobes (such as
Image.Resampling.LANCZOS) could result in byte corruption.

Because Lanczos weighting can create overshoots (ringing artifacts)
near sharp edges, the accumulated floating-point sum can sometimes
exceed the 16-bit maximum (65535) or fall below zero. Previously,
these out-of-bounds values were not correctly clamped before being
cast or packed into the 16-bit output buffer, leading to integer
overflow/underflow and corrupted pixels.

This update correctly clamps the accumulated float values to the
[0, 65535] range for I;16 images during resampling.
@hayatoikoma hayatoikoma marked this pull request as ready for review March 20, 2026 22:50
@hayatoikoma
Copy link
Copy Markdown
Author

@radarhere Codecov was failing and I added another test. But I couldn't figure out how I can run Codecov to check the result so it might be going to fail again.

Please let me know if there is anything else I should do to merge this PR. 👍🏼

ss_int = ROUND_UP(ss);
imOut->image8[yy][xx * 2 + (bigendian ? 1 : 0)] = CLIP8(ss_int % 256);
imOut->image8[yy][xx * 2 + (bigendian ? 0 : 1)] = CLIP8(ss_int >> 8);
if (ss_int < 0) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would make sense to have this in ImagingUtils.h:

#define CLIP16(v) ((v) <= 0 ? 0 : (v) < 65536 ? (v) : 65535)

and then

ss_int = CLIP16(ROUND_UP(ss));

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea! I updated.

@hayatoikoma
Copy link
Copy Markdown
Author

@wiredfool @radarhere Any additional action items for me? Do I need to squash the commits?

@radarhere
Copy link
Copy Markdown
Member

You don't need to squash the commits, no.

Did you use AI to develop this?

@hayatoikoma
Copy link
Copy Markdown
Author

Yes, I used AI.

@radarhere radarhere added the 🤖-assisted AI-assisted label Apr 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🤖-assisted AI-assisted

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants