The Exploit
An unauthenticated attacker with network access to a server running a vulnerable version of the Diffusers library can trigger memory exhaustion by sending a single crafted request.
import torch
from diffusers import DanceDiffusionPipeline
## Load the pipeline with default config
pipe = DanceDiffusionPipeline.from_pretrained("harmonai/maestro-512", safety_checker=None)
## Trigger memory exhaustion via unvalidated audio_length_in_s
pipe(audio_length_in_s=1e10)
When the request executes, the process will attempt to allocate a tensor of shape [1, 2, ~19200000] (approximately 150 GB for float32), causing either an immediate OutOfMemoryError or prolonged system thrashing before the process is killed by OOM killer. The same effect can be achieved via the pipeline's __call__ method with any large float value.
What the Patch Did
Before
if audio_length_in_s is None:
audio_length_in_s = self.unet.config.sample_size / self.unet.config.sample_rate
sample_size = audio_length_in_s * self.unet.config.sample_rate
down_scale_factor = 2 ** len(self.unet.up_blocks)
if sample_size < 3 * down_scale_factor:
raise ValueError(
f"{audio_length_in_s} is too small. Make sure it's bigger or equal to"
f" {3 * down_scale_factor / self.unet.config.sample_rate}."
)
original_sample_size = int(sample_size)
if sample_size % down_scale_factor != 0:
sample_size = (
(audio_length_in_s * self.unet.config.sample_rate) // down_scale_factor + 1
) * down_scale_factor
logger.info(...)
sample_size = int(sample_size)
After
if audio_length_in_s is None:
audio_length_in_s = self.unet.config.sample_size / self.unet.config.sample_rate
sample_size = audio_length_in_s * self.unet.config.sample_rate
down_scale_factor = 2 ** len(self.unet.up_blocks)
if sample_size < 3 * down_scale_factor:
raise ValueError(
f"{audio_length_in_s} is too small. Make sure it's bigger or equal to"
f" {3 * down_scale_factor / self.unet.config.sample_rate}."
)
## Added upper bound validation
max_sample_size = 60 * self.unet.config.sample_rate # 60 seconds maximum
if sample_size > max_sample_size:
raise ValueError(
f"{audio_length_in_s} exceeds maximum allowed audio length of 60 seconds."
)
original_sample_size = int(sample_size)
if sample_size % down_scale_factor != 0:
sample_size = (
(audio_length_in_s * self.unet.config.sample_rate) // down_scale_factor + 1
) * down_scale_factor
logger.info(...)
sample_size = int(sample_size)
The patch adds an explicit upper bound check on sample_size against a maximum of 60 seconds of audio (lines 99-102 in the fixed code). This is a classic validation constraint implemented via a ValueError raise, the same pattern used for the existing lower bound check.
Root Cause
CWE-400: Uncontrolled Resource Consumption ('Resource Exhaustion').
The dataflow begins with the audio_length_in_s parameter passed to DanceDiffusionPipeline.__call__(). This value, which comes directly from user input with no type validation beyond Python's dynamic typing, flows into the arithmetic sample_size = audio_length_in_s * self.unet.config.sample_rate. At no point between parameter reception and memory allocation is the computed sample_size checked against any upper bound. The result crosses the trust boundary from "user-controlled parameter" to "tensor dimension" when it reaches randn_tensor(shape=(batch_size, in_channels, sample_size), ...) on line 110 of the pipeline file. The existing lower bound check (sample_size < 3 * down_scale_factor) only prevents degenerate small values, making it entirely ineffectual against large values.
Why It Works
The single load-bearing line in the patch is if sample_size > max_sample_size:. If you removed this line, the entire patch becomes inert because the bug is purely a missing upper-bound check. The surrounding code (the max_sample_size constant definition and the ValueError block) serves only as supporting infrastructure. The engineer who wrote the patch correctly identified that the existing lower-bound check was insufficient—it prevented only undersized requests, not oversized ones. The engineer chose to implement a hard cap of 60 seconds rather than a dynamic validation based on available system memory, which would be fragile and system-dependent. Notably, the patch does not add any input-type validation (e.g., ensuring audio_length_in_s is a positive number), but in practice, negative values would still be caught by the lower-bound check.
Hardening Checklist
- Implement explicit upper bounds for all user-controlled numeric parameters that affect memory allocation. Use fixed values (like 60 seconds for audio) or dynamic checks based on available system resources.
- Set Python's
resource.setrlimit()for RLIMIT_AS in library-level entry points to provide a system-enforced memory ceiling that kills the process before OOM-killer is triggered. - Validate all user inputs at the API boundary with a schema validator (e.g., Pydantic or dataclass validation) to enforce both type and range constraints before any business logic executes.
- Audit all
randn_tensorandtorch.zeroscalls in the codebase for user-controlled dimensions, and ensure every dimension passed to these functions has been validated as finite and within expected bounds. - Log and limit the maximum execution time for individual pipeline calls using a watchdog timer to prevent long-running resource exhaustion from going undetected.
References
- https://nvd.nist.gov/vuln/detail/CVE-2026-44827
- https://nvd.nist.gov/vuln/detail/CVE-2026-45804
- https://nvd.nist.gov/vuln/detail/CVE-2026-44513