Check Point Research (CPR) identified three security vulnerabilities in the Graphics Device Interface (GDI) in Windows. We promptly reported these issues to Microsoft, and they were addressed in the Patch Tuesday updates in May, July, and August 2025.
These are the vulnerabilities:
Vulnerability disclosures such as these highlight the need for proactive measures to mitigate potential risks. Our purpose in publishing this blog after security fixes were implemented is to further raise awareness of these vulnerabilities and provide Windows users with defensive insights and mitigation recommendations. In the following sections, we detail the findings of our fuzzing campaign, which targeted Windows GDI using the EMF format and led to the discovery of these security vulnerabilities.
We found three separate crashes related to the processing of EmfPlusDrawString, EmfPlusFillRects and EmfPlusFillClosedCurve records. All three cases have a common root cause: another record sets the stage for exploitation. However, the outcome varies depending on which additional records are processed during the execution. Our current analysis focuses on the crash involving the EmfPlusDrawString record.
Multiple access violation exceptions occurred in the ScanOperation::AlphaMultiply_sRGB(), ScanOperation::Blend_sRGB_sRGB_MMX() and EpAntialiasedFiller::OutputSpan() functions within version 10.0.26100.3037 of the GdiPlus.dll module. These exceptions were triggered when the system attempted to read or write memory at the end of a 4000/0xFA0 bytes heap block, or while attempting to access reserved but unallocated memory.
This vulnerability could potentially allow a remote attacker to perform out-of-bounds read or write memory operations using a specially crafted EMF+ metafile. Figure 1 shows the decompiled source code of the ScanOperation::AlphaMultiply_sRGB() function at the time of the crash.

Figure 1. Decompiled source code of the affected ScanOperation::AlphaMultiply_sRGB() function.
In our crash sample, which serves as a proof of concept (PoC) for reproducing a vulnerability, an EmfPlusClear record is located before the EmfPlusDrawString record within the metafile. This record clears the output coordinate space and initializes it with a background color and transparency, as defined by its Color field. The field contains an EmfPlusARGB object specifying red, green, blue, and alpha components. This detail is significant because it allows an attacker to control the value written to memory during exploitation. Listing 1 shows the affected EmfPlusClear record.
EmfPlusClear clear = {
.Type = 0x4009,
.Flags = 0x0102,
.Size = 0x0000003c,
.DataSize = 0x00000030,
.Color = 0xaabbccff // Value written to memory
};
Listing 1. Sample EmfPlusClear record showing the value written to memory.
Further investigation revealed that the EmfPlusClear record handler uses the EpScanBitmap::Start() function to allocate a heap block to store 4000 bytes (0xFA0). This buffer is then populated with the specified EmfPlusARGB object, which undergoes alpha multiplication by the AlphaMultiply_sRGB() function during the processing of the EmfPlusDrawString record.
Note that the loop counter stored in the ebx register begins at 0x950. As each iteration writes a 4-byte object into the target buffer at ecx + edx, the function writes out-of-bounds after 1000 bytes, when the counter reaches 0x567. This behavior can be observed in Listing 2 which shows an excerpt from the crash analysis.
0:000> g (16b8.5ec): Access violation - code c0000005 (first/second chance not available) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=ffccbbaa ebx=00000567 ecx=022cec8c edx=08732374 esi=000015e3 edi=db000000 eip=74e16d9d esp=0075ddb0 ebp=0075ddc0 iopl=0 nv up ei ng nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000282 gdiplus!ScanOperation::AlphaMultiply_sRGB+0x2d: 74e16d9d 890411 mov dword ptr [ecx+edx],eax ds:002b:0aa01000=???????? 0:000> kb # ChildEBP RetAddr Args to Child 00 0075ddc0 74e16653 00000950 0872bd0c 0075e99c gdiplus!ScanOperation::AlphaMultiply_sRGB+0x2d 01 0075ddf4 74e16520 00000000 00000000 00000015 gdiplus!EpScanBitmap::NextBuffer+0xc3 02 0075de2c 74e15c91 00000000 00000000 00000015 gdiplus!DpOutputSolidColorSpan::OutputSpan+0x40 03 0075de58 74e188d4 00000000 ffffff19 00000955 gdiplus!DpClipRegion::OutputSpan+0x141 04 0075de7c 74e15988 00000000 0f47eed0 74e01b40 gdiplus!EpAliasedFiller::FillEdgesWinding+0x54 05 0075e938 74e0420e 00000001 00000003 00000000 gdiplus!RasterizePath+0x5d8 06 0075ea74 74e01cee 08747da0 0873cf98 0075eab8 gdiplus!DpDriver::StrokePath+0x3ee 07 0075ea9c 74e01db7 0075eab8 0075eb20 0075ede0 gdiplus!GpGraphics::DrvStrokePath+0x38 08 0075eadc 74df3b06 0075eb10 0075eb20 0075edcc gdiplus!GpGraphics::RenderDrawPath+0xbc 09 0075ed68 74e50a58 0075edcc 0075ed98 00000002 gdiplus!GpGraphics::DrawLines+0x148 0a 0075ee54 74f034c2 0aa08ff0 0f2a2fb8 00000000 gdiplus!FullTextImager::GdipLscbkDrawUnderline+0x228 0b 0075eee4 74ef7d57 0075f048 00000004 00000000 gdiplus!DrawUnderlineMerge+0x16f 0c 0075ef90 74eef9fd 00000001 00000000 00001015 gdiplus!DisplaySublineCore+0x25b 0d 0075f018 74ed907c 0aa40fa0 0aa40fa0 0aa40fa0 gdiplus!LsDisplayLine+0x193 0e 0075f02c 74edc08e 0075f048 00000000 0aa04e70 gdiplus!BuiltLine::Draw+0x12 0f 0075f058 74edbfd4 0aa40fa0 00000000 0aa04e70 gdiplus!FullTextImager::RenderLine+0x50 10 0075f0d8 74ed915f 0aa04e70 74ed9090 00000000 gdiplus!FullTextImager::Render+0x211 11 0075f108 74e6192a 0873ed28 0075f150 08744cb0 gdiplus!FullTextImager::Draw+0xcf 12 0075f3c8 74e8c55f 05b70184 00000014 0874bfe0 gdiplus!GpGraphics::DrawString+0x2f412 13 0075f404 74e02081 08744cb0 0000401c 0000df00 gdiplus!DrawStringEPR::Play+0xdf // Affects EmfPlusDrawString record 14 0075f428 74e01f97 0000401c 0000df00 00000044 gdiplus!GdipPlayMetafileRecordCallback+0x71 15 0075f45c 74e01e7c 00000104 05b700a8 0865ad58 gdiplus!MetafilePlayer::EnumerateEmfPlusRecords+0x97 16 0075f47c 75ca5d2f a901095a 0866cff8 05b70098 gdiplus!EnumEmfWithDownLevel+0x8c 17 0075f510 75ca4309 74e01df0 08744cb0 0075f58c gdi32full!bInternalPlayEMF+0x830 18 0075f524 771cad7f a901095a 824606f7 74e01df0 gdi32full!EnumEnhMetaFile+0x39 19 0075f544 74df5ed6 a901095a 824606f7 74e01df0 GDI32!EnumEnhMetaFileStub+0x2f 1a 0075f5a0 74df5b3b a901095a 824606f7 0075f5e4 gdiplus!MetafilePlayer::EnumerateEmfRecords+0xdf 1b 0075f648 74df81bc 08744cb0 824606f7 0075f774 gdiplus!GpGraphics::EnumEmfPlusDual+0x351 // Affects EMF+ Dual 1c 0075f7b8 74e1384c 0075f810 0075f810 00000002 gdiplus!GpMetafile::EnumerateForPlayback+0x819 1d 0075f8d8 74e01903 08723f28 0075f908 0075f918 gdiplus!GpGraphics::DrawImage+0x5ec 1e 0075f944 74e8a69e 08723f28 0075f96c 0075f97c gdiplus!GpGraphics::DrawImage+0x61 1f 0075f9a4 74e8b8a6 00000064 00000064 08723f28 gdiplus!GpMetafile::GetBitmap+0x1d2 20 0075f9b8 74e6e16b 00000064 00000064 00000000 gdiplus!GpMetafile::GetThumbnail+0x26 21 0075f9e0 006511a0 08723f28 00000064 00000064 gdiplus!GdipGetImageThumbnail+0x6b // Reachable via thumbnails <<<<<<<<<<<<<<unnecessary information removed>>>>>>>>>>>>>> 0:000> dd [ecx+edx]-0xfa0-10 L8 0aa00050 00000000 00000000 046e0b3c dcbabbbb 0aa00060 ffccbbaa ffccbbaa ffccbbaa ffccbbaa 0:000> dd [ecx+edx]-10 L8 0aa00ff0 ffccbbaa ffccbbaa ffccbbaa ffccbbaa 0aa01000 ???????? ???????? ???????? ????????
Listing 2. Stack trace showing an access violation in the ScanOperation::AlphaMultiply_sRGB() function.
Alpha values are multiplied by color values to ensure that transparent areas (where the alpha value is 0) do not contribute any color (R = G = B = 0). As a result, colors in semi-transparent areas appear darker because these objects emit less light compared to opaque ones.
The fourth byte is the alpha value, which can range from 0x00 to 0xFF. Setting the alpha value to 0xFF allows an attacker near-complete control over what is written to the buffer, as the RGB values remain unchanged. In the example above, B=0xAA, G=0xBB, R=0xCC and A=0xFF were written into the target buffer.

Figure 2. Binary differences showing the new ValidateAndSet() and IsRectValid() functions.
As illustrated by Figure 2, patch analysis confirmed that the crashes which occurred during the processing of various metafile records all shared a common root cause: the presence of invalid RECT objects within an EmfPlusSetTSClip record that preceded the other records in which the crashes were observed. Listing 3 shows the affected record from our crash samples.
EmfPlusSetTSClip clip = {
.Type = 0x403a,
.Flags = 0x0003, // Number of RECT objects
.Size = 0x00000048,
.DataSize = 0x0000003c,
.NumRects[3] = {
{ 0x00000005, 0x00000000, 0x04000000, 0x0000001f }, // Valid
{ 0x00000000, 0x00e90000, 0xfff70000, 0x00000000 }, // Not valid
{ 0x00000000, 0x00000000, 0x00000015, 0x003f8000 } // Valid
}
};
Listing 3. Sample EmfPlusSetTSClip record containing invalid RECT objects.
Processing invalid RECT objects results in a heap-based buffer overflow that may allow an attacker to perform out-of-bounds memory operations. An attacker could exploit this vulnerability by using various other metafile records to write or read memory, as a corrupted EmfPlusSetTSClip record sets the stage for exploitation. Looking at the rendering of the sample EmfPlusFillRects record in Figure 3 indicates that it is possible to disclose uninitialized or initialized heap bytes via the filled rectangle.

Figure 3. Leaking heap memory in Word via the sample EmfPlusFillRects record.
As we can see from the resulting image in Microsoft Word 365, the output varies in each rendering, which demonstrates that this crash sample leaks memory and eventually Word is unexpectedly terminated. This behavior already crosses a security boundary as it can lead to information disclosure if an attacker can read back the rendered image, for example, using JavaScript in a web browser.
Microsoft fixed this vulnerability within the SetTSClipEPR::Play() handler function in version 10.0.26100.4061 of GdiPlus.dll by introducing the ValidateAndSet() and IsRectValid() functions to validate RECT objects, as shown in Figure 4.

GdiPlus.dll to validate RECT objects.This bug was addressed with KB5058411 in the May 2025 Patch Tuesday as a remote code execution vulnerability of important severity, tracked as CVE-2025-30388. Notably, this issue also affects Microsoft Office for Mac and Android. In addition, MSRC’s exploitability assessment is “Exploitation More Likely”, indicating that this vulnerability presents a high-value target for attackers.
We identified a fourth crash while processing an EmfPlusDrawRects record. An access violation exception occurred in the ScanOperation::AlphaDivide_sRGB() function within version 10.0.26100.4202 of GdiPlus.dll, as it attempted to write to reserved but unallocated memory.
This vulnerability could allow a remote attacker to perform an out-of-bounds memory write using a specially crafted EMF+ metafile. Figure 5 shows the decompiled source code of the affected ScanOperation::AlphaDivide_sRGB() function at the time of the crash.

Figure 5. Decompiled source code of the ScanOperation::AlphaDivide_sRGB() function.
This issue is similar to CVE-2025-30388, the vulnerability we previously discussed. That bug was caused by invalid RECT objects within an EmfPlusSetTSClip record that preceded other records in which the crash occurred. In this new case, the vulnerability stems from a series of EmfPlusRect objects within the affected EmfPlusDrawRects record, detailed in Listing 4.
EmfPlusDrawRects rects = {
.Type = 0x400B,
.Flags = 0xEF00,
.Size = 0x00000048,
.DataSize = 0x0000003C,
.Count = 0x00000003,
.RectData[Count] = {
{ 0x0000, 0x3400, 0x3434, 0x3434 },
{ 0x3434, 0x3434, 0x3434, 0x3434 },
{ 0x3434, 0x3434, 0x3434, 0x3434 },
{ 0x3434, 0x3434, 0x0000, 0x3000 },
{ 0x3434, 0x3434, 0x3434, 0x3434 },
{ 0x3434, 0x3434, 0x0034, 0x0010 },
{ 0x4000, 0x3434, 0x3434, 0x3434 },
}
};
Listing 4. Sample EmfPlusDrawRects structure containing malformed EmfPlusRect objects.
The EmfPlusDrawRects record is preceded by an EmfPlusObject record that specifies an EmfPlusPen object used in graphics operations. The EmfPlusPen object defines a graphics brush associated with the pen. The brush is a solid-color brush, characterized by an EmfPlusARGB value. The source of the write operation in the eax register can be controlled by the attacker through this value, as shown by the excerpt of the crash analysis in Listing 5.
0:000> g (abc.7148): Access violation - code c0000005 (first/second chance not available) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=ffff004c ebx=000000ff ecx=fffcf63c edx=086cb9c4 esi=6af57480 edi=0868bd58 eip=6af574b3 esp=004feb34 ebp=004feb48 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 gdiplus!ScanOperation::AlphaDivide_sRGB+0x33: 6af574b3 890411 mov dword ptr [ecx+edx],eax ds:002b:0869b000=10000011 0:000> kb # ChildEBP RetAddr Args to Child 00 004feb48 6af573bd 00000035 0868bd0c 004fef98 gdiplus!ScanOperation::AlphaDivide_sRGB+0x33 01 004feb7c 6af5b364 00000064 00000063 00000064 gdiplus!EpScanBitmap::NextBuffer+0xdd 02 004febbc 6af5ab8e 004fef98 004fee40 00000001 gdiplus!OnePixelLineDDAAliased::DrawXMajor+0x54 03 004fecd4 6af5aabf 004fef98 00000000 00000000 gdiplus!DrawSolidLineOnePixelAliased+0x9e 04 004fed18 6af58bb8 004fefac 004fee20 00000005 gdiplus!DrawSolidStrokeOnePixel+0x9f 05 004fef48 6af3fe26 00000000 00000000 6af5aa20 gdiplus!FixedPointPathEnumerate+0x358 06 004feff8 6af405ac 086a7da0 0869cf98 004ff178 gdiplus!DpDriver::SolidStrokePathOnePixel+0x124 07 004ff134 6af43107 086a7da0 0869cf98 004ff178 gdiplus!DpDriver::StrokePath+0x33c 08 004ff15c 6af43096 004ff178 004ff1e8 086b1f8c gdiplus!GpGraphics::DrvStrokePath+0x3b 09 004ff19c 6af429c7 004ff1c4 004ff1e8 086b1f78 gdiplus!GpGraphics::RenderDrawPath+0xbc 0a 004ff39c 6afd3746 086b1f78 07bb0160 00000003 gdiplus!GpGraphics::DrawRects+0x356 0b 004ff3c4 6af40b59 086a4cb0 0000400b 00000400 gdiplus!DrawRectsEPR::Play+0x86 0c 004ff3e8 6af40a67 0000400b 00000400 0000003c gdiplus!GdipPlayMetafileRecordCallback+0x79 0d 004ff41c 6af4094c 00000104 07bb00a8 08006d58 gdiplus!MetafilePlayer::EnumerateEmfPlusRecords+0x97 0e 004ff43c 75eab76f 1c01154a 08d06ff8 07bb0098 gdiplus!EnumEmfWithDownLevel+0x8c 0f 004ff4d0 75ea9d49 6af408c0 086a4cb0 004ff54c gdi32full!bInternalPlayEMF+0x830 10 004ff4e4 75f2b9bf 1c01154a a94624e7 6af408c0 gdi32full!EnumEnhMetaFile+0x39 11 004ff504 6af368f6 1c01154a a94624e7 6af408c0 GDI32!EnumEnhMetaFileStub+0x2f 12 004ff560 6af36551 1c01154a a94624e7 004ff5a4 gdiplus!MetafilePlayer::EnumerateEmfRecords+0xdf 13 004ff608 6af38bdc 086a4cb0 a94624e7 004ff734 gdiplus!GpGraphics::EnumEmfPlusDual+0x351 14 004ff778 6af5454c 004ff7d0 004ff7d0 00000002 gdiplus!GpMetafile::EnumerateForPlayback+0x819 15 004ff898 6af467ac 08683f28 004ff8c8 004ff8d8 gdiplus!GpGraphics::DrawImage+0x5ec 16 004ff904 6afd198e 08683f28 004ff92c 004ff93c gdiplus!GpGraphics::DrawImage+0x61 17 004ff964 6afd2b96 00000064 00000064 08683f28 gdiplus!GpMetafile::GetBitmap+0x1d2 18 004ff978 6afb540b 00000064 00000064 00000000 gdiplus!GpMetafile::GetThumbnail+0x26 19 004ff9a0 009a11a0 08683f28 00000064 00000064 gdiplus!GdipGetImageThumbnail+0x6b <<<<<<<<<<<<<<unnecessary information removed>>>>>>>>>>>>>>
Listing 5. Stack trace showing an access violation in the ScanOperation::AlphaDivide_sRGB() function.
In a bitmap image, a scan-line is one horizontal row of pixels. Processing an image line by line means handling one scan-line at a time, from left to right and top to bottom. Further analysis showed that the EpScanBitmap::NextBuffer() function never verified that the number of scan-lines it was about to process fit in the destination bitmap, meaning that the function could be tricked into reading or writing past the bottom edge of an image if a call requested more scan-lines than existed.
Assuming that the bitmap allocated for thumbnail generation is 100×100 (0x64 × 0x64) pixels, the rectangle data in the PoC metafile deliberately pushes the scan position past the bottom edge of the bitmap and triggers the out-of-bounds write. Any one of those rectangles forces the rasterizer (which converts vector graphics into a pixel grid) to process scan-lines whose Y coordinate is well beyond the 0-99 range of the bitmap.


Microsoft fixed this vulnerability within the EpScanBitmap::NextBuffer() function in version 10.0.26100.4946 of GdiPlus.dll, as shown in Figure 6, by adding a check to detect when the requested number of scan-lines exceeds the height of the bitmap. The function now automatically trims the requested scan-lines to fit within the remaining rows, preventing any out-of-bounds access, as shown in Figure 7.

Figure 7. Decompiled source code of the patched GEpScanBitmap::NextBuffer() function.
This vulnerability was addressed with KB5063878 in the August 2025 Patch Tuesday as a critical severity remote code execution vulnerability tracked as CVE-2025-53766. Notably, this vulnerability requires no privileges or user interaction and can be exploited remotely over a network, making it a high-risk threat to web services that parse specially crafted metafiles.
We identified a fifth crash while processing an EMR_STARTDOC record, which immediately appeared to be related to the CVE-2022-35837 vulnerability. An access violation exception happened in the StringLengthWorkerW() function within version 10.0.26100.3624 of gdi32full.dll while attempting to read memory at the end of a 288/0x120 bytes heap block. Listing 6 contains the relevant excerpt of the crash analysis.
0:000> g (48b0.7b68): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=00dbf028 ebx=08195f7c ecx=08196000 edx=7fffffbc esi=7ffffffe edi=00000000 eip=7792d586 esp=00dbf014 ebp=00dbf01c iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202 gdi32full!StringLengthWorkerW+0xf: 7792d586 663939 cmp word ptr [ecx],di ds:002b:08196000=???? 0:000> kb # ChildEBP RetAddr Args to Child 00 00d3f37c 7796f55c 00d3f388 00000000 00d3f3cc gdi32full!StringLengthWorkerW+0xf 01 00d3f38c 77974155 00d3f3b0 0885bf4c 779740b0 gdi32full!StringCbLengthW+0x1a 02 00d3f3cc 77966ffb 9e011903 0885dff8 00000001 gdi32full!MRSTARTDOC::bPlay+0xa5 03 00d3f420 6d971f4d 9e011903 0885dff8 0885bf4c gdi32full!PlayEnhMetaFileRecord+0x5b 04 00d3f438 6d971e21 08ab5720 6d971c40 0000006b gdiplus!EmfEnumState::PlayRecord+0x2d 05 00d3f450 6d980b8c 0000006b 00000024 0885bf54 gdiplus!EmfEnumState::ProcessRecord+0x1e1 06 00d3f470 6d9b939d 0000006b 00000000 00000024 gdiplus!GdipPlayMetafileRecordCallback+0xec 07 00d3f49c 7796824f 9e011903 0885dff8 0885bf4c gdiplus!EnumEmfDownLevel+0x7d 08 00d3f530 77966829 6d9b9320 08ab2cb0 00d3f5ac gdi32full!bInternalPlayEMF+0x830 09 00d3f544 7632ad7f 9e011903 65461856 6d9b9320 gdi32full!EnumEnhMetaFile+0x39 0a 00d3f564 6d9768c6 9e011903 65461856 6d9b9320 GDI32!EnumEnhMetaFileStub+0x2f 0b 00d3f5c0 6d9741ac 9e011903 65461856 00d3f664 gdiplus!MetafilePlayer::EnumerateEmfRecords+0xdf 0c 00d3f678 6d9789eb 08ab2cb0 65461856 00d3f7b4 gdiplus!GpGraphics::EnumEmf+0x413 0d 00d3f7f8 6d99452c 00d3f850 00d3f850 00000002 gdiplus!GpMetafile::EnumerateForPlayback+0x658 0e 00d3f918 6d98676c 08a93f28 00d3f948 00d3f958 gdiplus!GpGraphics::DrawImage+0x5ec 0f 00d3f984 6da108fe 08a93f28 00d3f9ac 00d3f9bc gdiplus!GpGraphics::DrawImage+0x61 10 00d3f9e4 6da11b06 00000064 00000064 08a93f28 gdiplus!GpMetafile::GetBitmap+0x1d2 11 00d3f9f8 6d9f439b 00000064 00000064 00000000 gdiplus!GpMetafile::GetThumbnail+0x26 12 00d3fa20 00db11aa 08a93f28 00000064 00000064 gdiplus!GdipGetImageThumbnail+0x6b <<<<<<<<<<<<<<unnecessary information removed>>>>>>>>>>>>>>
Listing 6. Stack trace showing an access violation in the StringLengthWorkerW() function.
The stack trace suggests that the issue may lie with the StringLengthWorkerW() function, which performs a length check on user-controlled data and assumes the input is a null-terminated string. However, if the provided string is not null-terminated, the function may read beyond the allocated buffer, leading to potential information disclosure.
The decompiled source code of the MRSTARTDOC::bPlay() function shown in Figure 8 demonstrates that CVE-2022-35837 was addressed by assigning values to the lpszDocName and lpszOutput fields through a calculated offset stored in the v5 variable, if their respective fields are non-null.

Figure 8. Decompiled source code of the MRSTARTDOC::bPlay() function.
Listing 7 shows the affected EMR_STARTDOC metafile record. The DOCINFO structure contains the input and output file names and other information used by the StartDoc() function, which starts a print job.
EMR_STARTDOC startDoc = {
.Type = 0x0000006b,
.Size = 0x0000002c,
.docInfo = {
.cbSize = 0x00000020,
.lpszDocName = 0x2b464d45, // -> 0x1002
.lpszOutput = 0x00004001, // -> 0x6b14
.lpszDatatype = 0x0000001c,
.fwType = 0x00000010
}
};
Listing 7. Sample EMR_STARTDOC record with a DOCINFO structure.
The raw EMR_STARTDOC record shown in Listing 8 demonstrates that after patching the originally reported arbitrary information disclosure, the lpszDocName field points to 0x1002, located immediately after the record at offset 1Ch. Meanwhile, the lpszOutput field points to 0x6b14, positioned at offset 1Ch + 14h = 30h:
0000h 6B 00 00 00 2C 00 00 00 20 00 00 00 45 4D 46 2B k...,... ...EMF+ 0010h 01 40 00 00 1C 00 00 00 10 00 00 00 02 10 C0 DB .@............ÀÛ 0020h 01 00 00 05 05 7F FF 00 60 00 00 00 46 00 00 00 .....ÿ.`...F... 0030h 14 6B 6B 6B 6B 6B 6B 6B 6B 6B 6B 6B 6B 6B 5C 6B .kkkkkkkkkkkkk\k 0040h 6B 6B 6B 6B 6B 6B 6B 6B 6B 10 6B 6B 6B 6B 6B 6B kkkkkkkkk.kkkkkk 0050h 6B 6B 6B 6B kkkk
Listing 8. Raw EMR_STARTDOC record showing the calculated offsets.
The out-of-bounds read occurs because the MRSTARTDOC::bPlay() function validates string offsets inside the record. A specially crafted metafile may pad the first string in the lpszDocName field so that it nearly reaches the end of the record. After copying that string, the code advances its internal cursor beyond that point, but the next validation for the lpszOutput field still treats the supplied offset as if it were relative to the original base of the record.
This discrepancy allows an attacker to provide a value that passes the MR::bValidOff() function while actually pointing outside the heap block. Because no null terminator is found, the StringLengthWorkerW() function continues reading into adjacent memory, exposing its contents. The crash sample intensifies the issue by setting the EMF_HEADER.nBytes field to just 0x120 (288) bytes, causing the allocated buffer to be smaller than the embedded data and guaranteeing an over-read.
Microsoft fixed this vulnerability within the MRSTARTDOC::bPlay() handler function in version 10.0.26100.4652 of gdi32full.dll by correcting the offset arithmetic. The patched function now converts the pointer back to an offset relative to the start of the record before revalidating it and applies the same logic to the lpszOutput field. Therefore, the check matches the data that will be dereferenced, as shown in Figure 9.

Figure 9. Decompiled source code of the patched MRSTARTDOC::bPlay() function.
This bug was addressed with KB5062553 in the July 2025 Patch Tuesday as an important severity information disclosure vulnerability tracked as CVE-2025-47984. MSRC classified it as CWE-693: Protection Mechanism Failure, which in this case really means that the security fix to address CVE-2022-35837 was incomplete.
We discovered vulnerabilities in Windows GDI that could have serious implications for system security. Our extensive investigation into EMF+ files shows that staying ahead of potential threats requires continuous diligence and adaptation. By sharing these findings, we hope to raise awareness and provide valuable insights and recommendations to enhance security for all Windows users.
Security vulnerabilities can persist undetected for years, often resurfacing due to incomplete fixes. A particular information disclosure vulnerability, despite being formally addressed with a security patch, remained active for years due to the original issue receiving only a partial fix. This example underscores a basic conundrum for researchers: introducing a vulnerability is often easy, fixing it can be difficult, and verifying that a fix is both thorough and effective is even more challenging.
These issues highlight why comprehensive and continuous security testing, using verification techniques that must be constantly updated and improved, is crucial. This effort can be greatly enhanced by close collaboration between vendors and security researchers, including sharing planned fixes with the researchers who initially reported the issue. Such a collaborative approach adds an extra layer of review, helping to catch potential gaps early and strengthens the overall security of the software ecosystem.