Avoid out-of-bounds read in wxXPMDecoder::ReadFile on unterminated /* comment (Issue #26442)

12 views
Skip to first unread message

MarkLee131

unread,
May 10, 2026, 6:03:49 PM (4 days ago) May 10
to wx-...@googlegroups.com, Subscribed
MarkLee131 created an issue (wxWidgets/wxWidgets#26442)

Description

Bug description:

When wxXPMDecoder::ReadFile scans for the */ that closes a /* comment, the inner loop only stops at either */ or the buffer's null terminator. If the comment is unterminated, q ends pointing at the trailing \0, and the next line does strlen(q + 2) + 1, which reads past the allocated wxCharBuffer until it finds another null byte somewhere on the heap.

src/common/xpmdecod.cpp:166-174:

for (q = p + 2; *q != '\0'; q++)
{
    if ( (*q == '*') && (*(q + 1) == '/') )
        break;
}

// memmove allows overlaps (unlike strcpy):
size_t cpylen = strlen(q + 2) + 1;     // line 173 — reads past buffer end
memmove(p, q + 2, cpylen);

The buffer is allocated at line 133 (wxCharBuffer xpm_buffer(srcLen + 1)); its accessible range is [xpm_buffer, xpm_buffer + srcLen] with the trailing null at srcLen. When q == xpm_buffer + srcLen, q + 2 is two bytes past the allocation's end and strlen walks until it finds an unrelated null byte.

Found by an ASAN libFuzzer harness against wxXPMHandler::LoadFile.

Expected vs observed behaviour:

An XPM whose /* ... */ comment is missing the closing */ should be rejected or have the rest of the buffer skipped. Currently strlen reads past the allocation, and the subsequent memmove writes those out-of-bounds bytes back into the buffer.

Stack trace:

==1==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7c280762827f
READ of size 1 at 0x7c280762827f thread T0
    #0 strlen  sanitizer_common_interceptors.inc:425:5
    #1 wxXPMDecoder::ReadFile(wxInputStream&)  src/common/xpmdecod.cpp:173:25
    #2 wxXPMHandler::LoadFile(wxImage*, wxInputStream&, bool, int)  src/common/imagxpm.cpp:95:27
    #3 LLVMFuzzerTestOneInput  tests/fuzz/xpm.cpp:13:7
0x7c280762827f is located 1 bytes after 62-byte region [0x7c2807628240,0x7c280762827e)
allocated by:
    #1 wxCharTypeBuffer<char>::wxCharTypeBuffer(unsigned long)  include/wx/buffer.h:340:43
    #3 wxXPMDecoder::ReadFile(wxInputStream&)  src/common/xpmdecod.cpp:133:18
SUMMARY: AddressSanitizer: heap-buffer-overflow src/common/xpmdecod.cpp:173:25 in wxXPMDecoder::ReadFile

Patch or snippet allowing to reproduce the problem:

Drop the following as tests/fuzz/xpm.cpp next to the existing tests/fuzz/runner.cpp:

#include "wx/log.h"
#include "wx/mstream.h"
#include "wx/imagxpm.h"

extern "C" int LLVMFuzzerTestOneInput(const wxUint8* data, size_t size)
{
    wxLogNull noLog;
    wxMemoryInputStream mis(data, size);
    wxImage img;
    wxXPMHandler h;
    h.LoadFile(&img, mis, false);
    return 0;
}

61-byte PoC, hex below (an XPM-like payload that opens a /* comment without closing it). Reconstruct with xxd -r -p hex.txt poc.xpm:

2f2a20a8afb2dfd5d0dfce2f0a746174696320636861745b5d3d7b7b0a203120
312031222c0a226120632023666666666666222c0a22612261227d3b0a

To Reproduce:

  1. Check out master at the current HEAD and apply the harness above to tests/fuzz/xpm.cpp.

  2. Save the hex above and decode: xxd -r -p hex.txt poc.xpm.

  3. Build:

    clang++ -g -fsanitize=address tests/fuzz/{xpm,runner}.cpp \
        `wx-config --cxxflags --libs core,base` -o repro_xpm
    
  4. Run ./repro_xpm poc.xpm and observe the ASAN trace.

Suggested fix

After the inner loop, detect the unterminated case before strlen:

if (*q == '\0')
    break;     // unterminated /*-comment; stop processing

Patch ready with an inline-bytes Catch2 regression test. Happy to send a PR.

Platform and version information

  • wxWidgets version: master at HEAD f57b17ba5f63db53dd1b465646ade105fee22302. Line 173 is unchanged at tag v3.3.2, and the same pattern is present at v3.2.0.
  • wxWidgets port: wxGTK.
  • OS: Ubuntu 22.04 (gcr.io/oss-fuzz-base/base-builder).


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.
You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/26442@github.com>

VZ

unread,
May 10, 2026, 7:29:51 PM (4 days ago) May 10
to wx-...@googlegroups.com, Subscribed

Closed #26442 as completed via f429679.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issue/26442/issue_event/25360562705@github.com>

Reply all
Reply to author
Forward
0 new messages