CVE-2021-20224
Description
Integer overflow in ImageMagick's ExportIndexQuantum() can cause undefined behavior or crash when processing crafted PDF files.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Integer overflow in ImageMagick's ExportIndexQuantum() can cause undefined behavior or crash when processing crafted PDF files.
Vulnerability
An integer overflow vulnerability exists in ImageMagick's ExportIndexQuantum() function in MagickCore/quantum-export.c. The GetPixelIndex() function can return values outside the range of representable values for unsigned char. When these values are directly cast to unsigned char without an intermediate cast to ssize_t, the truncation can lead to undefined behavior. This vulnerability affects ImageMagick versions prior to the commits that introduced the ssize_t cast ([1], [3]).
Exploitation
An attacker can exploit this vulnerability by providing a crafted PDF file to an ImageMagick instance. No special privileges or authentication are required. When ImageMagick processes the malicious PDF, the ExportIndexQuantum() function is called, and the integer overflow occurs, potentially leading to crashes or other undefined behavior.
Impact
Successful exploitation can result in undefined behavior, including application crashes (denial of service). The full extent of impact is not documented, but it may also lead to information disclosure or other undefined outcomes.
Mitigation
The issue has been fixed in the ImageMagick codebase by casting the result of GetPixelIndex() to ssize_t before converting to unsigned char ([1], [3]). Users should update to a version of ImageMagick that includes this fix (e.g., commits 553054c1cb1e4e05ec86237afef76a32cd7c464d and 5af1dffa4b6ab984b5f13d1e91c95760d75f12a6). No workaround is available other than applying the patch or upgrading.
AI Insight generated on May 24, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
13(expand)+ 1 more
- (no CPE)
- (no CPE)
- osv-coords11 versionspkg:rpm/opensuse/ImageMagick&distro=openSUSE%20Leap%2015.3pkg:rpm/opensuse/ImageMagick&distro=openSUSE%20Leap%2015.4pkg:rpm/suse/ImageMagick&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP1-LTSSpkg:rpm/suse/ImageMagick&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Desktop%20Applications%2015%20SP3pkg:rpm/suse/ImageMagick&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Development%20Tools%2015%20SP3pkg:rpm/suse/ImageMagick&distro=SUSE%20Linux%20Enterprise%20Server%2012%20SP5pkg:rpm/suse/ImageMagick&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP1-LTSSpkg:rpm/suse/ImageMagick&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2012%20SP5pkg:rpm/suse/ImageMagick&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015%20SP1pkg:rpm/suse/ImageMagick&distro=SUSE%20Linux%20Enterprise%20Software%20Development%20Kit%2012%20SP5pkg:rpm/suse/ImageMagick&distro=SUSE%20Linux%20Enterprise%20Workstation%20Extension%2012%20SP5
< 7.0.7.34-150200.10.36.1+ 10 more
- (no CPE)range: < 7.0.7.34-150200.10.36.1
- (no CPE)range: < 7.0.7.34-150200.10.36.1
- (no CPE)range: < 7.0.7.34-150000.3.123.1
- (no CPE)range: < 7.0.7.34-150200.10.36.1
- (no CPE)range: < 7.0.7.34-150200.10.36.1
- (no CPE)range: < 6.8.8.1-71.180.1
- (no CPE)range: < 7.0.7.34-150000.3.123.1
- (no CPE)range: < 6.8.8.1-71.180.1
- (no CPE)range: < 7.0.7.34-150000.3.123.1
- (no CPE)range: < 6.8.8.1-71.180.1
- (no CPE)range: < 6.8.8.1-71.180.1
Patches
41 file changed · +1 −1
ChangeLog+1 −1 modified@@ -1,5 +1,5 @@ 2021-01-09 7.0.10-57 <quetzlzacatenango@image...> - * Release ImageMagick version 7.0.10-57 GIT revision 18218:d7a3d75ff:20210109 + * Release ImageMagick version 7.0.10-57 GIT revision 18221:2f611d533:20210109 2021-01-08 7.0.10-57 <quetzlzacatenango@image...> * update automake/autoconf configuration files.
1 file changed · +1 −1
ChangeLog+1 −1 modified@@ -1,5 +1,5 @@ 2021-01-09 6.9.11-57 <quetzlzacatenango@image...> - * Release ImageMagick version 6.9.11-57 GIT revision 16299:653bda373:20210109 + * Release ImageMagick version 6.9.11-57 GIT revision 16301:c2f75ef89:20210109 2021-01-08 6.9.11-57 <quetzlzacatenango@image...> * update automake/autoconf configuration files.
553054c1cb1ehttps://github.com/ImageMagick/ImageMagick/pull/3083
1 file changed · +37 −32
magick/quantum-export.c+37 −32 modified@@ -2516,21 +2516,21 @@ static void ExportIndexQuantum(const Image *image,QuantumInfo *quantum_info, for (x=((ssize_t) number_pixels-7); x > 0; x-=8) { - pixel=(unsigned char) *indexes++; + pixel=(unsigned char) ((ssize_t) *indexes++); *q=((pixel & 0x01) << 7); - pixel=(unsigned char) *indexes++; + pixel=(unsigned char) ((ssize_t) *indexes++); *q|=((pixel & 0x01) << 6); - pixel=(unsigned char) *indexes++; + pixel=(unsigned char) ((ssize_t) *indexes++); *q|=((pixel & 0x01) << 5); - pixel=(unsigned char) *indexes++; + pixel=(unsigned char) ((ssize_t) *indexes++); *q|=((pixel & 0x01) << 4); - pixel=(unsigned char) *indexes++; + pixel=(unsigned char) ((ssize_t) *indexes++); *q|=((pixel & 0x01) << 3); - pixel=(unsigned char) *indexes++; + pixel=(unsigned char) ((ssize_t) *indexes++); *q|=((pixel & 0x01) << 2); - pixel=(unsigned char) *indexes++; + pixel=(unsigned char) ((ssize_t) *indexes++); *q|=((pixel & 0x01) << 1); - pixel=(unsigned char) *indexes++; + pixel=(unsigned char) ((ssize_t) *indexes++); *q|=((pixel & 0x01) << 0); q++; } @@ -2539,7 +2539,7 @@ static void ExportIndexQuantum(const Image *image,QuantumInfo *quantum_info, *q='\0'; for (bit=7; bit >= (ssize_t) (8-(number_pixels % 8)); bit--) { - pixel=(unsigned char) *indexes++; + pixel=(unsigned char) ((ssize_t) *indexes++); *q|=((pixel & 0x01) << (unsigned char) bit); } q++; @@ -2553,15 +2553,15 @@ static void ExportIndexQuantum(const Image *image,QuantumInfo *quantum_info, for (x=0; x < (ssize_t) (number_pixels-1) ; x+=2) { - pixel=(unsigned char) *indexes++; + pixel=(unsigned char) ((ssize_t) *indexes++); *q=((pixel & 0xf) << 4); - pixel=(unsigned char) *indexes++; + pixel=(unsigned char) ((ssize_t) *indexes++); *q|=((pixel & 0xf) << 0); q++; } if ((number_pixels % 2) != 0) { - pixel=(unsigned char) *indexes++; + pixel=(unsigned char) ((ssize_t) *indexes++); *q=((pixel & 0xf) << 4); q++; } @@ -2669,25 +2669,25 @@ static void ExportIndexAlphaQuantum(const Image *image, for (x=((ssize_t) number_pixels-3); x > 0; x-=4) { - pixel=(unsigned char) *indexes++; + pixel=(unsigned char) ((ssize_t) *indexes++); *q=((pixel & 0x01) << 7); pixel=(unsigned char) (GetPixelOpacity(p) == (Quantum) TransparentOpacity ? 1 : 0); *q|=((pixel & 0x01) << 6); p++; - pixel=(unsigned char) *indexes++; + pixel=(unsigned char) ((ssize_t) *indexes++); *q|=((pixel & 0x01) << 5); pixel=(unsigned char) (GetPixelOpacity(p) == (Quantum) TransparentOpacity ? 1 : 0); *q|=((pixel & 0x01) << 4); p++; - pixel=(unsigned char) *indexes++; + pixel=(unsigned char) ((ssize_t) *indexes++); *q|=((pixel & 0x01) << 3); pixel=(unsigned char) (GetPixelOpacity(p) == (Quantum) TransparentOpacity ? 1 : 0); *q|=((pixel & 0x01) << 2); p++; - pixel=(unsigned char) *indexes++; + pixel=(unsigned char) ((ssize_t) *indexes++); *q|=((pixel & 0x01) << 1); pixel=(unsigned char) (GetPixelOpacity(p) == (Quantum) TransparentOpacity ? 1 : 0); @@ -2700,7 +2700,7 @@ static void ExportIndexAlphaQuantum(const Image *image, *q='\0'; for (bit=3; bit >= (ssize_t) (4-(number_pixels % 4)); bit-=2) { - pixel=(unsigned char) *indexes++; + pixel=(unsigned char) ((ssize_t) *indexes++); *q|=((pixel & 0x01) << (unsigned char) (bit+4)); pixel=(unsigned char) (GetPixelOpacity(p) == (Quantum) TransparentOpacity ? 1 : 0); @@ -2718,10 +2718,10 @@ static void ExportIndexAlphaQuantum(const Image *image, for (x=0; x < (ssize_t) number_pixels ; x++) { - pixel=(unsigned char) *indexes++; + pixel=(unsigned char) ((ssize_t) *indexes++); *q=((pixel & 0xf) << 4); - pixel=(unsigned char) (16*QuantumScale*((Quantum) (QuantumRange- - GetPixelOpacity(p)))+0.5); + pixel=(unsigned char) ((ssize_t) (16*QuantumScale*((Quantum) + (QuantumRange-GetPixelOpacity(p)))+0.5)); *q|=((pixel & 0xf) << 0); p++; q++; @@ -2752,7 +2752,8 @@ static void ExportIndexAlphaQuantum(const Image *image, { for (x=0; x < (ssize_t) number_pixels; x++) { - q=PopShortPixel(quantum_info->endian,(unsigned short) GetPixelIndex(indexes+x),q); + q=PopShortPixel(quantum_info->endian,(unsigned short) + ((ssize_t) GetPixelIndex(indexes+x)),q); pixel=SinglePrecisionToHalf(QuantumScale*GetPixelAlpha(p)); q=PopShortPixel(quantum_info->endian,pixel,q); p++; @@ -2762,7 +2763,8 @@ static void ExportIndexAlphaQuantum(const Image *image, } for (x=0; x < (ssize_t) number_pixels; x++) { - q=PopShortPixel(quantum_info->endian,(unsigned short) GetPixelIndex(indexes+x),q); + q=PopShortPixel(quantum_info->endian,(unsigned short) + ((ssize_t) GetPixelIndex(indexes+x)),q); pixel=ScaleQuantumToShort((Quantum) (QuantumRange-GetPixelOpacity(p))); q=PopShortPixel(quantum_info->endian,pixel,q); p++; @@ -2792,7 +2794,8 @@ static void ExportIndexAlphaQuantum(const Image *image, } for (x=0; x < (ssize_t) number_pixels; x++) { - q=PopLongPixel(quantum_info->endian,(unsigned int) GetPixelIndex(indexes+x),q); + q=PopLongPixel(quantum_info->endian,(unsigned int) + GetPixelIndex(indexes+x),q); pixel=ScaleQuantumToLong((Quantum) (QuantumRange-GetPixelOpacity(p))); q=PopLongPixel(quantum_info->endian,pixel,q); p++; @@ -2827,10 +2830,9 @@ static void ExportIndexAlphaQuantum(const Image *image, range=GetQuantumRange(quantum_info->depth); for (x=0; x < (ssize_t) number_pixels; x++) { - q=PopQuantumPixel(quantum_info, - GetPixelIndex(indexes+x),q); - q=PopQuantumPixel(quantum_info, - ScaleQuantumToAny((Quantum) (GetPixelAlpha(p)),range),q); + q=PopQuantumPixel(quantum_info,GetPixelIndex(indexes+x),q); + q=PopQuantumPixel(quantum_info,ScaleQuantumToAny((Quantum) + (GetPixelAlpha(p)),range),q); p++; q+=quantum_info->pad; } @@ -3035,8 +3037,8 @@ static void ExportRedQuantum(QuantumInfo *quantum_info, range=GetQuantumRange(quantum_info->depth); for (x=0; x < (ssize_t) number_pixels; x++) { - q=PopQuantumPixel(quantum_info, - ScaleQuantumToAny(GetPixelRed(p),range),q); + q=PopQuantumPixel(quantum_info,ScaleQuantumToAny(GetPixelRed(p),range), + q); p++; q+=quantum_info->pad; } @@ -3150,7 +3152,8 @@ static void ExportRGBQuantum(QuantumInfo *quantum_info, break; } } - q=PopShortPixel(quantum_info->endian,(unsigned short) (pixel << 4),q); + q=PopShortPixel(quantum_info->endian,(unsigned short) (pixel << 4), + q); switch ((x+1) % 3) { default: @@ -3171,7 +3174,8 @@ static void ExportRGBQuantum(QuantumInfo *quantum_info, break; } } - q=PopShortPixel(quantum_info->endian,(unsigned short) (pixel << 4),q); + q=PopShortPixel(quantum_info->endian,(unsigned short) (pixel << 4), + q); q+=quantum_info->pad; } for (bit=0; bit < (ssize_t) (3*number_pixels % 2); bit++) @@ -3196,7 +3200,8 @@ static void ExportRGBQuantum(QuantumInfo *quantum_info, break; } } - q=PopShortPixel(quantum_info->endian,(unsigned short) (pixel << 4),q); + q=PopShortPixel(quantum_info->endian,(unsigned short) (pixel << 4), + q); q+=quantum_info->pad; } if (bit != 0)
5af1dffa4b6aoutside the range of representable values of type 'unsigned char' (#3083)
1 file changed · +13 −13
MagickCore/quantum-export.c+13 −13 modified@@ -2530,28 +2530,28 @@ static void ExportIndexQuantum(const Image *image,QuantumInfo *quantum_info, for (x=((ssize_t) number_pixels-7); x > 0; x-=8) { - pixel=(unsigned char) GetPixelIndex(image,p); + pixel=(unsigned char) ((ssize_t) GetPixelIndex(image,p)); *q=((pixel & 0x01) << 7); p+=GetPixelChannels(image); - pixel=(unsigned char) GetPixelIndex(image,p); + pixel=(unsigned char) ((ssize_t) GetPixelIndex(image,p)); *q|=((pixel & 0x01) << 6); p+=GetPixelChannels(image); - pixel=(unsigned char) GetPixelIndex(image,p); + pixel=(unsigned char) ((ssize_t) GetPixelIndex(image,p)); *q|=((pixel & 0x01) << 5); p+=GetPixelChannels(image); - pixel=(unsigned char) GetPixelIndex(image,p); + pixel=(unsigned char) ((ssize_t) GetPixelIndex(image,p)); *q|=((pixel & 0x01) << 4); p+=GetPixelChannels(image); - pixel=(unsigned char) GetPixelIndex(image,p); + pixel=(unsigned char) ((ssize_t) GetPixelIndex(image,p)); *q|=((pixel & 0x01) << 3); p+=GetPixelChannels(image); - pixel=(unsigned char) GetPixelIndex(image,p); + pixel=(unsigned char) ((ssize_t) GetPixelIndex(image,p)); *q|=((pixel & 0x01) << 2); p+=GetPixelChannels(image); - pixel=(unsigned char) GetPixelIndex(image,p); + pixel=(unsigned char) ((ssize_t) GetPixelIndex(image,p)); *q|=((pixel & 0x01) << 1); p+=GetPixelChannels(image); - pixel=(unsigned char) GetPixelIndex(image,p); + pixel=(unsigned char) ((ssize_t) GetPixelIndex(image,p)); *q|=((pixel & 0x01) << 0); p+=GetPixelChannels(image); q++; @@ -2561,7 +2561,7 @@ static void ExportIndexQuantum(const Image *image,QuantumInfo *quantum_info, *q='\0'; for (bit=7; bit >= (ssize_t) (8-(number_pixels % 8)); bit--) { - pixel=(unsigned char) GetPixelIndex(image,p); + pixel=(unsigned char) ((ssize_t) GetPixelIndex(image,p)); *q|=((pixel & 0x01) << (unsigned char) bit); p+=GetPixelChannels(image); } @@ -2576,17 +2576,17 @@ static void ExportIndexQuantum(const Image *image,QuantumInfo *quantum_info, for (x=0; x < (ssize_t) (number_pixels-1) ; x+=2) { - pixel=(unsigned char) GetPixelIndex(image,p); + pixel=(unsigned char) ((ssize_t) GetPixelIndex(image,p)); *q=((pixel & 0xf) << 4); p+=GetPixelChannels(image); - pixel=(unsigned char) GetPixelIndex(image,p); + pixel=(unsigned char) ((ssize_t) GetPixelIndex(image,p)); *q|=((pixel & 0xf) << 0); p+=GetPixelChannels(image); q++; } if ((number_pixels % 2) != 0) { - pixel=(unsigned char) GetPixelIndex(image,p); + pixel=(unsigned char) ((ssize_t) GetPixelIndex(image,p)); *q=((pixel & 0xf) << 4); p+=GetPixelChannels(image); q++; @@ -2597,7 +2597,7 @@ static void ExportIndexQuantum(const Image *image,QuantumInfo *quantum_info, { for (x=0; x < (ssize_t) number_pixels; x++) { - q=PopCharPixel((unsigned char) GetPixelIndex(image,p),q); + q=PopCharPixel((unsigned char) ((ssize_t) GetPixelIndex(image,p)),q); p+=GetPixelChannels(image); q+=quantum_info->pad; }
Vulnerability mechanics
Root cause
"Missing intermediate cast to `ssize_t` before truncating `GetPixelIndex()` return values to `unsigned char`, causing integer overflow when index values exceed the representable range."
Attack vector
An attacker supplies a crafted PDF file that, when processed by ImageMagick, causes `GetPixelIndex()` to return index values outside the range representable by `unsigned char` [ref_id=2]. The direct cast `(unsigned char) GetPixelIndex(...)` truncates these values, leading to undefined behavior or a crash [patch_id=2271391]. The attack requires no special privileges beyond the ability to submit a malicious PDF for ImageMagick to decode and export.
Affected code
The vulnerability resides in `ExportIndexQuantum()` and `ExportIndexAlphaQuantum()` in `MagickCore/quantum-export.c` (ImageMagick 7) and `magick/quantum-export.c` (ImageMagick 6) [patch_id=2271391][patch_id=2271393]. The functions `GetPixelIndex()` and `*indexes++` return values that are directly cast to `unsigned char` without first being cast through `ssize_t`, causing values outside the representable range of `unsigned char` to be truncated [patch_id=2271391][ref_id=2].
What the fix does
The patch inserts an intermediate cast to `ssize_t` before the final cast to `unsigned char`, e.g. `(unsigned char) ((ssize_t) GetPixelIndex(image,p))` [patch_id=2271391][patch_id=2271393]. This ensures the value is first widened to a signed type large enough to hold the full range, preventing truncation of out-of-range index values. The same pattern is applied to `ExportIndexAlphaQuantum()` and related helper calls [patch_id=2271393].
Preconditions
- inputAttacker must supply a crafted PDF file that produces pixel index values outside the unsigned char range when processed by ImageMagick.
- configImageMagick must be invoked to export/convert the PDF (e.g., via `convert`, `identify`, or library API), triggering the ExportIndexQuantum or ExportIndexAlphaQuantum code path.
Generated on May 24, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4News mentions
0No linked articles in our index yet.