CacheReadString() takes the length of a string straight from a .hhp.cached file and passes len - 1 to wxCharBuffer.
src/html/helpdata.cpp:337:
inline static wxString CacheReadString(wxInputStream *f) { size_t len = (size_t)CacheReadInt32(f); wxCharBuffer str(len-1); f->Read(str.data(), len); return wxString(str, wxConvUTF8); }
include/wx/buffer.h:344:
wxCharTypeBuffer(size_t len) { CharType* const str = (CharType *)malloc((len + 1)*sizeof(CharType)); if ( str ) { str[len] = (CharType)0;
A stored length of 0 makes len - 1 wrap to SIZE_MAX, so malloc((len + 1)) wraps to malloc(0) and str[len] is str[SIZE_MAX], which on 64-bit is ptr - 1. CacheWriteString() always counts the trailing NUL, so 0 never occurs in a file wxWidgets wrote, but nothing rejects it on read.
Three more in the same function, all from the same unchecked length:
wxString ctor scans past the end when the data has no NUL of its ownsize_t, and malloc failing leaves Read() with a null pointer0x7FFFFFFF asks for a 2 GB allocation from a 24 byte payloadThe contents and index counts at :373 and :388 are also used unchecked, newsize = st + CacheReadInt32(f); feeding straight into Alloc() on the next line. A 12 byte .cached declaring 0x7FFFFFF0 items hangs and then dies on std::bad_alloc.
Expected: a .cached file that could not have been produced by SaveCachedBook() is rejected, and AddBookParam() falls back to the MS
project as it already does for a version mismatch.
Observed: the length is used as-is.
==108==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x50200000388f
WRITE of size 1 at 0x50200000388f thread T0
#0 wxCharTypeBuffer<char>::wxCharTypeBuffer(unsigned long) include/wx/buffer.h:349
#1 wxCharBuffer::wxCharBuffer(unsigned long) include/wx/buffer.h:444
#2 CacheReadString src/html/helpdata.cpp:340
#3 wxHtmlHelpData::LoadCachedBook(wxHtmlBookRecord*, wxInputStream*) src/html/helpdata.cpp:380
#4 wxHtmlHelpData::AddBookParam(...) src/html/helpdata.cpp:565
#5 wxHtmlHelpData::AddBook(wxString const&) src/html/helpdata.cpp:703
#6 wxHtmlHelpData::AddBook(wxString const&) src/html/helpdata.cpp:644
#7 main
0x50200000388f is located 1 bytes to the left of 1-byte region
allocated by thread T0 here:
#0 __interceptor_malloc
#1 wxCharTypeBuffer<char>::wxCharTypeBuffer(unsigned long) include/wx/buffer.h:346
#2 wxCharBuffer::wxCharBuffer(unsigned long) include/wx/buffer.h:444
#3 CacheReadString src/html/helpdata.cpp:340
It fires once per string field, so twice per contents item.
AddBook() accepts .htb, finds the .hhp inside it, and AddBookParam() then opens bookfile.GetLocation() + ".cached", which resolves to a second entry in the same archive. So one file drives all of it.
Build poc.htb:
import struct, zipfile def i32(v): return struct.pack('<i', v) # version 5, format flags 1, one contents item (level 0, id 0), # whose name field declares a length of 0. cached = i32(5) + i32(1) + i32(1) + i32(0) + i32(0) + i32(0) with zipfile.ZipFile('poc.htb', 'w') as z: z.writestr(zipfile.ZipInfo('doc.hhp', (2020, 1, 1, 0, 0, 0)), b'Title=poc\r\n') # must not be older than the .hhp or the cached file is ignored z.writestr(zipfile.ZipInfo('doc.hhp.cached', (2030, 1, 1, 0, 0, 0)), cached)
Load it:
#include "wx/init.h" #include "wx/filesys.h" #include "wx/fs_arc.h" #include "wx/html/helpdata.h" int main() { wxInitializer init; wxFileSystem::AddHandler(new wxArchiveFSHandler); wxHtmlHelpData data; data.AddBook("poc.htb"); return 0; }
python3 mkpoc.py to write poc.htb, 251 bytes.g++ -fsanitize=address -g repro.cpp -o repro $(wx-config --cxxflags --libs html,core,base)./repro prints the report above. Without a sanitizer the write lands in the allocator's bookkeeping instead.d5aacc4712, and 3.3.3. The same code is in 3.2.11 and in every release back to 3.2.9; git log -L traces it to the 2.4 merge--enable-html --enable-htmlhelp—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!
You are receiving this because you are subscribed to this thread.![]()
—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!
You are receiving this because you are subscribed to this thread.![]()