GHSA-9VJ4-WC7R-P844
Vulnerability from github – Published: 2026-01-21 01:05 – Updated: 2026-01-21 01:05Summary
Stack overflow via infinite recursion in MSL (Magick Scripting Language) <write> command when writing to MSL format.
Version
- ImageMagick 7.x (tested on current main branch)
- Commit: HEAD
- Requires: libxml2 support (for MSL parsing)
Steps to Reproduce
Method 1: Using ImageMagick directly
magick MSL:recursive.msl out.png
Method 2: Using OSS-Fuzz reproduce
python3 infra/helper.py build_fuzzers imagemagick
python3 infra/helper.py reproduce imagemagick msl_fuzzer recursive.msl
Or run the fuzzer directly:
./msl_fuzzer recursive.msl
Expected Behavior
ImageMagick should handle recursive MSL references gracefully by detecting the loop and returning an error.
Actual Behavior
Stack overflow causes process crash:
AddressSanitizer:DEADLYSIGNAL
==PID==ERROR: AddressSanitizer: stack-overflow
#0 MSLStartElement /src/imagemagick/coders/msl.c:7045
#1 xmlParseStartTag /src/libxml2/parser.c
#2 xmlParseChunk /src/libxml2/parser.c:11273
#3 ProcessMSLScript /src/imagemagick/coders/msl.c:7405
#4 WriteMSLImage /src/imagemagick/coders/msl.c:7867
#5 WriteImage /src/imagemagick/MagickCore/constitute.c:1346
#6 MSLStartElement /src/imagemagick/coders/msl.c:7045
... (infinite recursion, 287+ frames)
Root Cause Analysis
In coders/msl.c, the <write> command handler in MSLStartElement() (line ~7045) calls WriteImage(). When the output filename specifies MSL format (msl:filename), WriteMSLImage() is called, which parses the MSL file again via ProcessMSLScript().
If the MSL file references itself (directly or indirectly), this creates an infinite recursion loop:
MSLStartElement() → WriteImage() → WriteMSLImage() → ProcessMSLScript()
→ xmlParseChunk() → MSLStartElement() → ... (infinite loop)
Impact
- DoS: Guaranteed crash via stack exhaustion
- Affected: Any application using ImageMagick to process user-supplied MSL files
Additional Trigger Paths
The <read> command can also trigger recursion:
Indirect recursion is also possible (a.msl → b.msl → a.msl).
Fuzzer
This issue was discovered using a custom MSL fuzzer:
#include <cstdint>
#include <Magick++/Blob.h>
#include <Magick++/Image.h>
#include "utils.cc"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
{
if (IsInvalidSize(Size))
return(0);
try
{
const Magick::Blob blob(Data, Size);
Magick::Image image;
image.magick("MSL");
image.fileName("MSL:");
image.read(blob);
}
catch (Magick::Exception)
{
}
return(0);
}
This issue was found by Team FuzzingBrain @ Texas A&M University
{
"affected": [
{
"package": {
"ecosystem": "NuGet",
"name": "Magick.NET-Q8-x64"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "14.10.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "NuGet",
"name": "Magick.NET-Q8-arm64"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "14.10.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "NuGet",
"name": "Magick.NET-Q8-x86"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "14.10.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "NuGet",
"name": "Magick.NET-Q8-OpenMP-x64"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "14.10.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "NuGet",
"name": "Magick.NET-Q8-OpenMP-arm64"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "14.10.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "NuGet",
"name": "Magick.NET-Q16-x64"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "14.10.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "NuGet",
"name": "Magick.NET-Q16-arm64"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "14.10.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "NuGet",
"name": "Magick.NET-Q16-x86"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "14.10.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "NuGet",
"name": "Magick.NET-Q16-OpenMP-x64"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "14.10.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "NuGet",
"name": "Magick.NET-Q16-OpenMP-arm64"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "14.10.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "NuGet",
"name": "Magick.NET-Q16-HDRI-x64"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "14.10.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "NuGet",
"name": "Magick.NET-Q16-HDRI-arm64"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "14.10.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "NuGet",
"name": "Magick.NET-Q16-HDRI-x86"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "14.10.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "NuGet",
"name": "Magick.NET-Q16-HDRI-OpenMP-x64"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "14.10.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "NuGet",
"name": "Magick.NET-Q16-HDRI-OpenMP-arm64"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "14.10.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "NuGet",
"name": "Magick.NET-Q8-AnyCPU"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "14.10.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "NuGet",
"name": "Magick.NET-Q16-AnyCPU"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "14.10.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "NuGet",
"name": "Magick.NET-Q16-HDRI-AnyCPU"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "14.10.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-23874"
],
"database_specific": {
"cwe_ids": [
"CWE-835"
],
"github_reviewed": true,
"github_reviewed_at": "2026-01-21T01:05:23Z",
"nvd_published_at": "2026-01-20T01:15:57Z",
"severity": "MODERATE"
},
"details": "## Summary\n\nStack overflow via infinite recursion in MSL (Magick Scripting Language) `\u003cwrite\u003e` command when writing to MSL format.\n\n## Version\n\n- ImageMagick 7.x (tested on current main branch)\n- Commit: HEAD\n- Requires: libxml2 support (for MSL parsing)\n\n## Steps to Reproduce\n\n### Method 1: Using ImageMagick directly\n\n```bash\nmagick MSL:recursive.msl out.png\n```\n\n### Method 2: Using OSS-Fuzz reproduce\n\n```bash\npython3 infra/helper.py build_fuzzers imagemagick\npython3 infra/helper.py reproduce imagemagick msl_fuzzer recursive.msl\n```\n\nOr run the fuzzer directly:\n```bash\n./msl_fuzzer recursive.msl\n```\n\n## Expected Behavior\n\nImageMagick should handle recursive MSL references gracefully by detecting the loop and returning an error.\n\n## Actual Behavior\n\nStack overflow causes process crash:\n\n```\nAddressSanitizer:DEADLYSIGNAL\n==PID==ERROR: AddressSanitizer: stack-overflow\n #0 MSLStartElement /src/imagemagick/coders/msl.c:7045\n #1 xmlParseStartTag /src/libxml2/parser.c\n #2 xmlParseChunk /src/libxml2/parser.c:11273\n #3 ProcessMSLScript /src/imagemagick/coders/msl.c:7405\n #4 WriteMSLImage /src/imagemagick/coders/msl.c:7867\n #5 WriteImage /src/imagemagick/MagickCore/constitute.c:1346\n #6 MSLStartElement /src/imagemagick/coders/msl.c:7045\n ... (infinite recursion, 287+ frames)\n```\n\n## Root Cause Analysis\n\nIn `coders/msl.c`, the `\u003cwrite\u003e` command handler in `MSLStartElement()` (line ~7045) calls `WriteImage()`. When the output filename specifies MSL format (`msl:filename`), `WriteMSLImage()` is called, which parses the MSL file again via `ProcessMSLScript()`.\n\nIf the MSL file references itself (directly or indirectly), this creates an infinite recursion loop:\n\n```\nMSLStartElement() \u2192 WriteImage() \u2192 WriteMSLImage() \u2192 ProcessMSLScript()\n \u2192 xmlParseChunk() \u2192 MSLStartElement() \u2192 ... (infinite loop)\n```\n\n## Impact\n\n- **DoS**: Guaranteed crash via stack exhaustion\n- **Affected**: Any application using ImageMagick to process user-supplied MSL files\n\n## Additional Trigger Paths\n\nThe `\u003cread\u003e` command can also trigger recursion:\n\nIndirect recursion is also possible (a.msl \u2192 b.msl \u2192 a.msl).\n\n## Fuzzer\n\nThis issue was discovered using a custom MSL fuzzer:\n\n```cpp\n#include \u003ccstdint\u003e\n#include \u003cMagick++/Blob.h\u003e\n#include \u003cMagick++/Image.h\u003e\n#include \"utils.cc\"\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)\n{\n if (IsInvalidSize(Size))\n return(0);\n try\n {\n const Magick::Blob blob(Data, Size);\n Magick::Image image;\n image.magick(\"MSL\");\n image.fileName(\"MSL:\");\n image.read(blob);\n }\n catch (Magick::Exception)\n {\n }\n return(0);\n}\n```\n\nThis issue was found by Team FuzzingBrain @ Texas A\u0026M University",
"id": "GHSA-9vj4-wc7r-p844",
"modified": "2026-01-21T01:05:23Z",
"published": "2026-01-21T01:05:23Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/ImageMagick/ImageMagick/security/advisories/GHSA-9vj4-wc7r-p844"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-23874"
},
{
"type": "PACKAGE",
"url": "https://github.com/ImageMagick/ImageMagick"
},
{
"type": "WEB",
"url": "https://github.com/dlemstra/Magick.NET/releases/tag/14.10.2"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "ImageMagick MSL: Stack overflow via infinite recursion in ProcessMSLScript"
}
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.