Feb 16, 2026·5 min read·11 visits
ImageMagick's OpenCL device profiler leaks memory when parsing bad XML. By placing a malformed 'ImagemagickOpenCLDeviceProfile.xml' in the cache directory, an attacker can cause persistent memory leaks in long-running processes, eventually leading to Denial of Service.
A memory leak vulnerability exists in ImageMagick's OpenCL benchmarking component. When parsing malformed device profile XML files, the parser fails to release allocated string members within the benchmark structure, leading to resource exhaustion.
ImageMagick is the heavy lifter of the internet. If you've ever uploaded a photo to a website and it magically resized itself, ImageMagick probably did the work. To keep up with the demand for speed, the library tries to offload work to your GPU using OpenCL. But GPUs are finicky beasts, so ImageMagick runs a "benchmark" the first time it sees a device to figure out if it's worthy of the workload.
This benchmark data is cached in an XML file so it doesn't have to run every time. It sounds efficient, right? Well, that caching mechanism is where our story begins. In the rush to parse this XML and get to the GPU acceleration, the developers left a small but critical gap in their memory management logic. It’s a classic case of "happy path" programming—assuming the data is always good, and failing to clean up the mess when it isn't.
This isn't a remote code execution that will give you a shell in five seconds. It's a slow burn. A memory leak. It's the digital equivalent of a dripping faucet that eventually floods the basement. In long-running services that process thousands of images, this drip turns into a deluge.
The vulnerability lives in MagickCore/opencl.c, specifically within the LoadOpenCLDeviceBenchmark() function. This function is tasked with reading ImagemagickOpenCLDeviceProfile.xml. It iterates through the XML nodes, looking for <device> tags. When it finds one, it does what any good C program does: it asks the operating system for some memory.
It allocates a MagickCLDeviceBenchmark structure. Inside that structure, there are pointers to strings: platform_name, vendor_name, name, and version. These aren't just fixed buffers; they are dynamically allocated strings created via ConstantString().
Here is the logic flaw: The code assumes that if the XML parsing fails or hits a weird state (like a malformed tag), it should just free the parent structure (device_benchmark) and bail out. The problem is that freeing a struct in C does not free the pointers inside it. It's like demolishing a house but leaving all the furniture floating in mid-air. The parent container is gone, but the string allocations remain in the heap, orphaned and unreachable.
Let's look at the crime scene. In the vulnerable version of MagickCore/opencl.c, the cleanup block at the end of the function (or during error handling) looked something like this:
// The Vulnerable Exit Block
// 1. Free the temporary token string
token=(char *) RelinquishMagickMemory(token);
// 2. Free the benchmark struct itself
device_benchmark=(MagickCLDeviceBenchmark *)
RelinquishMagickMemory(device_benchmark);
// WAIT! What about device_benchmark->platform_name?
// What about device_benchmark->vendor_name?
// They are gone. Leaked forever.The fix is straightforward but tedious. You have to manually walk through the struct and free every member before you free the struct itself. This is why languages like Rust are eating C's lunch, but I digress. Here is the corrected logic:
// The Fixed Cleanup Logic
if (device_benchmark != (MagickCLDeviceBenchmark *) NULL)
{
// Manually free every string member 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);
// ... repeat for name and version ...
// Finally, free the parent struct
device_benchmark=(MagickCLDeviceBenchmark *)
RelinquishMagickMemory(device_benchmark);
}To exploit this, we don't need to overflow a buffer; we just need to lie to the parser. The parser expects a well-formed XML entry like <device name="..." />. If we give it a <device tag that opens but never closes, or contains garbage, the parser hits the error path—the same error path that forgets to free memory.
The attack vector is local, which limits the fun, but it's deadly in shared environments. The target file is typically located at ~/.cache/ImageMagick/ImagemagickOpenCLDeviceProfile.xml. If you are on a multi-user system or a web server where you can write files to the working directory or the user's home (perhaps via a separate unrestricted file upload vulnerability), you can drop this time bomb.
The Payload:
<managed_devices>
<!-- Open the tag, allocate the memory, but never close it -->
<device
platform="LeakyPlatformString_That_Takes_Up_Space"
vendor="LeakyVendorString_That_Takes_Up_Space"
name="LeakyName_That_Takes_Up_Space"
version="1.0" Every time the ImageMagick library initializes (which could be every time a PHP script processes an upload), it reads this file. It allocates the strings. It hits the malformed syntax. It errors out. It leaks the strings. Rinse and repeat until the server runs out of RAM and the OOM killer steps in.
The remediation is simple: update. The Magick.NET team (specifically dlemstra) identified this and patched it in version 14.10.2. For the core C library, you'll want to be on 7.1.2-13 or later.
If you can't update immediately, you have two options. First, verify permissions on the ImageMagick cache directory. Ensure that the user running the ImageMagick process is the only one who can write to it. If an attacker can't place the XML file, they can't trigger the leak.
Second, turn off the feature entirely. If you aren't doing heavy lifting that requires OpenCL (and let's be honest, most simple web resizing tasks don't), you can disable it via environment variables or the policy.xml configuration. No OpenCL means no benchmarking, which means no XML parsing, which means no leak.
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
ImageMagick ImageMagick Studio LLC | <= 7.1.2-12 | 7.1.2-13 |
Magick.NET dlemstra | < 14.10.2 | 14.10.2 |
| Attribute | Detail |
|---|---|
| CWE | CWE-401 (Memory Leak) |
| Attack Vector | Local (File System) |
| CVSS | 6.5 (Medium) |
| Component | MagickCore/opencl.c |
| Function | LoadOpenCLDeviceBenchmark |
| Exploit Status | Proof of Concept Available |
The software does not sufficiently track and release allocated memory after it has been used, which effectively reduces available memory over time.