GHSA-QP59-X883-77QV

Leaking Bytes in the Fast Lane: ImageMagick OpenCL DoS

Alon Barad
Alon Barad
Software Engineer

Jan 21, 2026·4 min read·2 visits

Executive Summary (TL;DR)

ImageMagick's `opencl.c` fails to properly free allocated string members within a struct when parsing malformed OpenCL device profile XMLs. While the struct container is freed, the internal pointers remain allocated, leading to a memory leak. Repeated triggering can cause Denial of Service (DoS) via OOM.

A memory leak vulnerability in ImageMagick's OpenCL device benchmark loader allows attackers to exhaust system resources via malformed XML profiles.

The Hook: The Price of Speed

ImageMagick is the heavy lifter of the internet's image processing infrastructure. From PHP extensions to command-line cron jobs, it is everywhere. To keep up with the demand for speed, ImageMagick leverages OpenCL to offload processing to GPUs and accelerators. It's a noble goal: crunch pixels faster.

But hardware acceleration requires configuration. To track performance, ImageMagick benchmarks available OpenCL devices and caches the results in an XML file, typically residing in ~/.cache/ImageMagick/ImagemagickOpenCLDeviceProfile.xml. This file acts as a persistent memory of the hardware's capabilities.

Here is the catch: Parsing XML in C is notoriously painful. It requires manual memory management for every tag, attribute, and text node. And in the LoadOpenCLDeviceBenchmark function, the developers made a classic mistake: they focused on the container and forgot about the contents.

The Flaw: A Container Without a Bottom

The vulnerability lies within MagickCore/opencl.c. When ImageMagick initializes, it reads the benchmark XML to avoid re-running expensive tests. The function responsible, LoadOpenCLDeviceBenchmark, iterates through the file looking for <device> tags.

When it finds a device, it allocates a MagickCLDeviceBenchmark structure. As it parses attributes like platform, vendor, name, and version, it allocates dynamic memory for each string using ConstantString(). These pointers are stored inside the struct.

Here is the logic flaw: The code assumes a happy path where the XML tag is closed properly (e.g., />). If the tag is malformed—specifically, if it is never closed—the parser hits an error condition or simply reaches the end of the loop. In this exit path, the code creates a 'zombie' struct. It frees the device_benchmark struct itself, but it fails to free the strings pointed to by the struct members before destroying it. The container is gone, but the data it held is left floating in the heap, causing a memory leak.

The Code: The Smoking Gun

Let's look at the vulnerable logic in MagickCore/opencl.c. The parser allocates memory for the attributes as soon as it sees them:

/* Inside the parsing loop */
if (LocaleCompare(keyword,"name") == 0)
  {
    /* Allocates memory for 'name' */
    device_benchmark->name=ConstantString(token);
    continue;
  }
// ... similar logic for platform, vendor, etc.

When the loop terminates (potentially prematurely due to malformed XML), the cleanup code looks like this:

/* The Vulnerable Cleanup */
token=(char *) RelinquishMagickMemory(token);
/* The struct is freed... */
device_benchmark=(MagickCLDeviceBenchmark *) RelinquishMagickMemory(device_benchmark);
/* ...but device_benchmark->name is NOT freed! */

This is the programming equivalent of demolishing a house but leaving all the furniture hovering in mid-air. The pointer to the name string is inside device_benchmark. Once you free device_benchmark, you lose the address of name, making it impossible to free later.

The Exploit: Poisoning the Cache

To exploit this, we don't need complex heap feng shui or ROP chains. We just need to feed ImageMagick a bad configuration file. The target is the OpenCL cache file.

An attacker can craft a file named ImagemagickOpenCLDeviceProfile.xml with a device tag that opens but never closes:

<opencl-device-profile>
  <!-- Open the tag, allocate the strings, but never close it with /> -->
  <device platform="LeakMe" vendor="LeakMeVendor" name="LeakMeName" version="1.0"
</opencl-device-profile>

When ImageMagick runs (even for a simple operation like converting a blank image), it parses this file. It allocates memory for "LeakMe", "LeakMeVendor", etc. It reaches the end of the file looking for the closing bracket />, doesn't find it, and hits the cleanup routine. The strings are leaked.

Using AddressSanitizer (ASAN), we can see the exact carnage:

==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 16 byte(s) in 1 object(s) allocated from:
    #1 ... in LoadOpenCLDeviceBenchmark MagickCore/opencl.c:878

While ~200 bytes per run sounds trivial, this accumulates rapidly in environments where ImageMagick is spawned repeatedly (e.g., a web worker processing user uploads) or in long-running daemon processes that reload configurations.

The Fix: Clean Up Your Mess

The remediation is straightforward: you must free the members before you free the struct. The maintainers patched this by explicitly relinquishing memory for every string attribute before destroying the parent object.

Here is the corrected logic:

/* The Corrected Cleanup */
if (device_benchmark != (MagickCLDeviceBenchmark *) NULL)
{
  /* Free the members first */
  if (device_benchmark->platform_name != (char *) NULL)
    device_benchmark->platform_name=RelinquishMagickMemory(device_benchmark->platform_name);
  if (device_benchmark->vendor_name != (char *) NULL)
    device_benchmark->vendor_name=RelinquishMagickMemory(device_benchmark->vendor_name);
  if (device_benchmark->name != (char *) NULL)
    device_benchmark->name=RelinquishMagickMemory(device_benchmark->name);
  if (device_benchmark->version != (char *) NULL)
    device_benchmark->version=RelinquishMagickMemory(device_benchmark->version);
 
  /* Then free the struct */
  device_benchmark=(MagickCLDeviceBenchmark *) RelinquishMagickMemory(device_benchmark);
}

This ensures that regardless of how the parsing ends (success or malformed error), the heap is returned to a clean state.

Fix Analysis (1)

Technical Appendix

CVSS Score
6.5/ 10
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:H

Affected Systems

ImageMagick 7.1.2-12 and earlierMagick.NET prior to 14.10.2

Affected Versions Detail

Product
Affected Versions
Fixed Version
ImageMagick
ImageMagick Studio LLC
<= 7.1.2-127.1.2-13
Magick.NET
dlemstra
< 14.10.214.10.2
AttributeDetail
CWE IDCWE-401 (Memory Leak)
Attack VectorLocal / Network (File Context)
CVSS6.5 (Medium)
ImpactDenial of Service (DoS)
ComponentOpenCL Device Benchmark Loader
Exploit StatusPoC Available
CWE-401
Memory Leak

Missing Release of Memory after Effective Lifetime

Vulnerability Timeline

Vulnerability Published
2026-01-21
Patch Released in 7.1.2-13
2026-01-21

Subscribe to updates

Get the latest CVE analysis reports delivered to your inbox.