Memory leak in ReadInlineImage on unsupported "data:" URI types (regression from 71489bb)
ImageMagick version
7.1.2-32
Operating system
Linux
Operating system, version and so on
Linux (Ubuntu 22.04, x86_64), built with clang/gcc + AddressSanitizer (Q16-HDRI)
Description
There is a memory leak in ReadInlineImage() (MagickCore/constitute.c) whenever an inline "data:" image uses a MIME subtype that is not a registered/authorized format.
ReadInlineImage() first allocates:
- blob = Base64Decode(...)
- read_info = CloneImageInfo(image_info) // ~13 KB ImageInfo
then validates the media type:
if (GetImplicitDataImageType(content, read_info->magick, exception) == MagickFalse)
ThrowReaderException(ImageError, "ImageTypeNotSupported");ThrowReaderException (MagickCore/exception-private.h:76) expands to a plain return ((Image *) NULL), so on this validation failure both read_info and blob are leaked. The cleanup lines at the end of the function (RelinquishMagickMemory(blob); DestroyImageInfo(read_info);) are never reached.
This looks like a regression introduced by commit 71489bb ("deny inline implicit image formats", 2026-08-02, part of GHSA-3rjr-534c-8v67): the new GetImplicitDataImageType() check was inserted after the allocations but its failure path returns without freeing them. The leak is structural (control flow guarantees it), not layout dependent.
Impact: ~13 KB per triggering input. A single CLI invocation has little impact (memory is reclaimed at process exit), but long-running processes that batch-decode untrusted SVG (web upload previews, server-side thumbnailers) will grow memory without bound when fed images with unsupported data: URIs. Please assess whether this warrants a security advisory; at minimum it is a small, safe fix.
Steps to Reproduce
PoC file "poc_inline_unknown.svg":
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
<image width="24" height="24" xlink:href="data:image/zzz;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=="/>
</svg>Reproduce with an ASan build (any of: cmake --preset fuzzing, or a plain -fsanitize=address build). Decode the same file twice in one process so the leak is reported by LeakSanitizer at exit:
magick poc_inline_unknown.svg poc_inline_unknown.svg info:Expected output (ASan/LeakSanitizer report):
==PID==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 13024 byte(s) in 1 object(s) allocated from:
#0 ... in malloc
#1 ... in AcquireMagickMemory .../MagickCore/memory.c:537
#2 ... in AcquireCriticalMemory .../MagickCore/memory.c:620
#3 ... in AcquireImageInfo .../MagickCore/image.c:341
#4 ... in CloneImageInfo .../MagickCore/image.c:958
#5 ... in ReadInlineImage .../MagickCore/constitute.c:1194
#6 ... in SVGEndElement .../coders/svg.c:2751
#7 ... libxml2
Direct leak of 72 byte(s) in 1 object(s) allocated from: ...Equivalent minimal C driver (no CLI needed), link against an ASan MagickCore build:
#include "MagickCore/MagickCore.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char** argv) {
if (argc < 2) return 1;
FILE* f = fopen(argv[1], "rb");
fseek(f, 0, SEEK_END); long n = ftell(f); fseek(f, 0, SEEK_SET);
unsigned char* b = malloc((size_t)n);
fread(b, 1, (size_t)n, f);
fclose(f);
MagickCoreGenesis(NULL, MagickFalse);
ImageInfo* info = AcquireImageInfo();
(void) strcpy(info->filename, "blob:");
ExceptionInfo* ex = AcquireExceptionInfo();
for (int i = 0; i < 2; i++) {
Image* im = BlobToImage(info, b, (size_t)n, ex);
if (im) DestroyImageList(im);
ClearMagickException(ex);
}
DestroyImageInfo(info);
DestroyExceptionInfo(ex);
MagickCoreTerminus();
free(b);
return 0;
}Note: decoding the file only once does not report a leak (single-shot process-exit path); two decodes in one process reproduce it deterministically. Controls: a valid data:image/png;base64 URI does not leak; a zero-length base64 payload returns before the allocation and does not leak.
Affected entry points (same defect, all call ReadInlineImage()):
- coders/svg.c:2751 - SVG (shown above)
- coders/inline.c:135,177 - INLINE coder
- MagickCore/draw.c:5777 - text primitive with a data: URI
Images
No image can be attached: the bug lives on the decode-rejection path of an inline data: URI, so no output image is ever produced. The complete reproducer is the 6-line SVG text above; the base64 payload inside it is a valid 1x1 PNG, only the declared MIME subtype "image/zzz" is unsupported, which is what drives the leak path.
Source: ImageMagick/ImageMagick