Skip to content

resize_to_fill can force a multi-billion-pixel intermediate allocation from a tiny extreme-aspect-ratio source image #3083

Description

@scadastrangelove

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind: oomThis bug allocates unbounded / too much memorykind: slowNot wrong, but still unusable

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions