Summary
DynamicImage::resize_to_fill's intermediate resize step can be forced to allocate far more memory than either the source image or the requested target size, for source images with an extreme aspect ratio — a legitimately small, valid image can drive a multi-billion-pixel intermediate allocation.
Details
resize_to_fill computes its intermediate size via math::utils::resize_dimensions(width, height, nwidth, nheight, fill=true):
let ratio = if fill { f64::max(wratio, hratio) } else { f64::min(wratio, hratio) };
let nw = (width as f64 * ratio).round();
let nh = (height as f64 * ratio).round();
For fill=true, ratio = max(wratio, hratio) — the intermediate size is scaled so that one dimension exactly matches the target while the other overshoots proportionally to the aspect-ratio mismatch between source and destination, before a final crop trims the excess.
Concretely: a source image of 1×1,000,000 pixels (a tiny, entirely legitimate 1-pixel-wide image, valid in essentially every image format) resized to fill a normal 1000×1000 target: wratio = 1000/1 = 1000, hratio = 1000/1,000,000 = 0.001, ratio = max(1000, 0.001) = 1000. The intermediate size becomes nw = 1*1000 = 1000, nh = 1,000,000*1000 = 1,000,000,000 — a 1000×1,000,000,000-pixel (1 billion pixel) intermediate image, before the final crop reduces it back to 1000×1000.
This intermediate is additionally represented as Rgba32FImage internally (vertical_sample/horizontal_sample, 16 bytes/pixel) regardless of the source's actual bit depth — a further per-pixel memory multiplier on top of the dimension amplification.
(Not independently run to completion — the point being illustrated is exactly the multi-GB/multi-billion-pixel allocation this arithmetic implies, so actually executing it would itself be the resource-exhaustion event being described. The formula is deterministic and was traced by hand from the published math/utils.rs source, not assumed.)
Impact
Any application offering user-controllable "resize to fill a target box" functionality (e.g. thumbnail/avatar generation) on untrusted source images is vulnerable to a memory-exhaustion DoS from a tiny, valid, extreme-aspect-ratio image — no malformed bytes required.
Suggested direction
Not submitting a PR — this needs a maintainer's design call: cap the intermediate size independent of the final target (e.g. bound the overshoot ratio), reject source images whose aspect ratio diverges from the target by more than some threshold, or document the risk and let callers validate source dimensions themselves before calling resize_to_fill.
Discovered by the rust-in-peace security pipeline.
Summary
DynamicImage::resize_to_fill's intermediate resize step can be forced to allocate far more memory than either the source image or the requested target size, for source images with an extreme aspect ratio — a legitimately small, valid image can drive a multi-billion-pixel intermediate allocation.Details
resize_to_fillcomputes its intermediate size viamath::utils::resize_dimensions(width, height, nwidth, nheight, fill=true):For
fill=true,ratio = max(wratio, hratio)— the intermediate size is scaled so that one dimension exactly matches the target while the other overshoots proportionally to the aspect-ratio mismatch between source and destination, before a final crop trims the excess.Concretely: a source image of 1×1,000,000 pixels (a tiny, entirely legitimate 1-pixel-wide image, valid in essentially every image format) resized to fill a normal 1000×1000 target:
wratio = 1000/1 = 1000,hratio = 1000/1,000,000 = 0.001,ratio = max(1000, 0.001) = 1000. The intermediate size becomesnw = 1*1000 = 1000,nh = 1,000,000*1000 = 1,000,000,000— a 1000×1,000,000,000-pixel (1 billion pixel) intermediate image, before the final crop reduces it back to 1000×1000.This intermediate is additionally represented as
Rgba32FImageinternally (vertical_sample/horizontal_sample, 16 bytes/pixel) regardless of the source's actual bit depth — a further per-pixel memory multiplier on top of the dimension amplification.(Not independently run to completion — the point being illustrated is exactly the multi-GB/multi-billion-pixel allocation this arithmetic implies, so actually executing it would itself be the resource-exhaustion event being described. The formula is deterministic and was traced by hand from the published
math/utils.rssource, not assumed.)Impact
Any application offering user-controllable "resize to fill a target box" functionality (e.g. thumbnail/avatar generation) on untrusted source images is vulnerable to a memory-exhaustion DoS from a tiny, valid, extreme-aspect-ratio image — no malformed bytes required.
Suggested direction
Not submitting a PR — this needs a maintainer's design call: cap the intermediate size independent of the final target (e.g. bound the overshoot ratio), reject source images whose aspect ratio diverges from the target by more than some threshold, or document the risk and let callers validate source dimensions themselves before calling
resize_to_fill.Discovered by the rust-in-peace security pipeline.