I ran Claude Code to look for potential issues, including logic bugs, copy-paste errors, architecture problems, panics, and similar issues.
I don't have much experience with low-level C and assembly, so I wasn't able to properly verify most of these findings myself. However, I reported a similar set of findings to the FEX project, and the author fixed most of the issues I reported(FEX-Emu/FEX#5678).
The full report - box64_20260807.html
Here are a few example findings that I'm almost certain are actual bugs:
CPY_4 CRITICAL
Description: my32_XFreeFontSet frees set->names[i] in both cleanup loops; the second loop iterates over
set->fonts_size but still frees from the set->names array. When a program called both
XFontsOfFontSet(..., fonts, names) variants, the same set->names[i] block is passed to
box32_free() twice (double free / heap corruption), the set->fonts[i] blocks are leaked, and if
fonts_size > names_size the loop reads past the end of set->names.
Locations:
|
EXPORT void my32_XFreeFontSet(x64emu_t* emu,void* dpy, my_XFontSet_32_t* set) |
|
{ |
|
my->XFreeFontSet(dpy, set->fontset); |
|
for(int i=0; i<set->names_size; ++i) |
|
box32_free(set->names[i]); |
|
box32_free(set->names); |
|
for(int i=0; i<set->fonts_size; ++i) |
|
box32_free(set->names[i]); |
|
box32_free(set->fonts); |
|
box32_free(set); |
|
} |
Fix:
for(int i=0; i<set->fonts_size; ++i)
- box32_free(set->names[i]);
+ box32_free(set->fonts[i]);
box32_free(set->fonts);
CPY_49 HIGH
Description: my32_glBindBuffersRange converts both the offsets and the sizes arrays from 32-bit long_t to native
64-bit long, but then passes the unconverted guest array sizes to the native entry point instead of
sizes_l. The native driver therefore reads count 64-bit GLsizeiptr values out of a buffer that only holds
count 32-bit values: every size is wrong (two adjacent 32-bit sizes merged into one 64-bit value) and the read
runs 4 * count bytes past the end of the guest array. The very next functions in the same file
(my32_glBindVertexBuffers, my32_glVertexArrayVertexBuffers) pass their converted array correctly, which
makes this an obvious editing slip. The bug is duplicated verbatim in wrappedlibglxnvidia.c.
Locations:
|
EXPORT void my32_glBindBuffersRange(x64emu_t* emu, uint32_t target, uint32_t first, int count, void* buffers, ptr_t* offsets, ptr_t* sizes) |
|
{ |
|
vFuuippp_t fnc = getBridgeFnc2((void*)R_RIP); |
|
if(!fnc) fnc=my->glBindBuffersRange; |
|
long offsets_l[count]; |
|
long sizes_l[count]; |
|
for(int i=0; i<count; ++i) { |
|
offsets_l[i] = from_long(offsets[i]); |
|
sizes_l[i] = from_long(sizes[i]); |
|
} |
|
fnc(target, first, count, buffers, offsets_l, sizes); |
|
} |
|
EXPORT void my32nv_glBindBuffersRange(x64emu_t* emu, uint32_t target, uint32_t first, int count, void* buffers, ptr_t* offsets, ptr_t* sizes) |
|
{ |
|
vFuuippp_t fnc = getBridgeFnc2((void*)R_RIP); |
|
if(!fnc) fnc=my->glBindBuffersRange; |
|
long offsets_l[count]; |
|
long sizes_l[count]; |
|
for(int i=0; i<count; ++i) { |
|
offsets_l[i] = from_long(offsets[i]); |
|
sizes_l[i] = from_long(sizes[i]); |
|
} |
|
fnc(target, first, count, buffers, offsets_l, sizes); |
|
} |
Fix: Before: fnc(target, first, count, buffers, offsets_l, sizes);
After: fnc(target, first, count, buffers, offsets_l, sizes_l);
Apply the same one-word change in my32nv_glBindBuffersRange.
CPY_75 MEDIUM
Description: In all four xcb connection converters the xid.max field is filled from source->xid.last instead of
source->xid.max. last and max are different members of my_xcb_xid_t / x64_xcb_xid_t
(last is the last handed-out XID, max is the end of the currently reserved range), so after any conversion the
guest/host sees max == last, which makes libxcb believe the XID range is exhausted on every allocation.
The bug is duplicated in align_xcb_connection, unalign_xcb_connection, align_xcb_connection32 and
unalign_xcb_connection32.
Locations:
|
dest->xid.base = source->xid.base; |
|
dest->xid.inc = source->xid.inc; |
|
dest->xid.last = source->xid.last; |
|
dest->xid.max = source->xid.last; |
|
dest->xid.base = source->xid.base; |
|
dest->xid.inc = source->xid.inc; |
|
dest->xid.last = source->xid.last; |
|
dest->xid.max = source->xid.last; |
|
dest->xid.base = source->xid.base; |
|
dest->xid.inc = source->xid.inc; |
|
dest->xid.last = source->xid.last; |
|
dest->xid.max = source->xid.last; |
|
dest->xid.base = source->xid.base; |
|
dest->xid.inc = source->xid.inc; |
|
dest->xid.last = source->xid.last; |
|
dest->xid.max = source->xid.last; |
Fix:
// before
dest->xid.max = source->xid.last;
// after
dest->xid.max = source->xid.max;
Apply in all four places.
I ran Claude Code to look for potential issues, including logic bugs, copy-paste errors, architecture problems, panics, and similar issues.
I don't have much experience with low-level C and assembly, so I wasn't able to properly verify most of these findings myself. However, I reported a similar set of findings to the FEX project, and the author fixed most of the issues I reported(FEX-Emu/FEX#5678).
The full report - box64_20260807.html
Here are a few example findings that I'm almost certain are actual bugs:
CPY_4
CRITICALDescription:
my32_XFreeFontSetfreesset->names[i]in both cleanup loops; the second loop iterates overset->fonts_sizebut still frees from theset->namesarray. When a program called bothXFontsOfFontSet(..., fonts, names)variants, the sameset->names[i]block is passed tobox32_free()twice (double free / heap corruption), theset->fonts[i]blocks are leaked, and iffonts_size > names_sizethe loop reads past the end ofset->names.Locations:
box64/src/wrapped32/wrappedlibx11.c
Lines 2569 to 2579 in 2f130fa
Fix:
CPY_49
HIGHDescription:
my32_glBindBuffersRangeconverts both theoffsetsand thesizesarrays from 32-bitlong_tto native64-bit
long, but then passes the unconverted guest arraysizesto the native entry point instead ofsizes_l. The native driver therefore readscount64-bitGLsizeiptrvalues out of a buffer that only holdscount32-bit values: every size is wrong (two adjacent 32-bit sizes merged into one 64-bit value) and the readruns
4 * countbytes past the end of the guest array. The very next functions in the same file(
my32_glBindVertexBuffers,my32_glVertexArrayVertexBuffers) pass their converted array correctly, whichmakes this an obvious editing slip. The bug is duplicated verbatim in
wrappedlibglxnvidia.c.Locations:
box64/src/wrapped32/wrappedlibgl.c
Lines 401 to 412 in 2f130fa
box64/src/wrapped32/wrappedlibglxnvidia.c
Lines 402 to 413 in 2f130fa
Fix: Before:
fnc(target, first, count, buffers, offsets_l, sizes);After:
fnc(target, first, count, buffers, offsets_l, sizes_l);Apply the same one-word change in
my32nv_glBindBuffersRange.CPY_75
MEDIUMDescription: In all four xcb connection converters the
xid.maxfield is filled fromsource->xid.lastinstead ofsource->xid.max.lastandmaxare different members ofmy_xcb_xid_t/x64_xcb_xid_t(
lastis the last handed-out XID,maxis the end of the currently reserved range), so after any conversion theguest/host sees
max == last, which makes libxcb believe the XID range is exhausted on every allocation.The bug is duplicated in
align_xcb_connection,unalign_xcb_connection,align_xcb_connection32andunalign_xcb_connection32.Locations:
box64/src/libtools/myalign.c
Lines 1660 to 1663 in 2f130fa
box64/src/libtools/myalign.c
Lines 1698 to 1701 in 2f130fa
box64/src/libtools/myalignxcb32.c
Lines 113 to 116 in 2f130fa
box64/src/libtools/myalignxcb32.c
Lines 175 to 178 in 2f130fa
Fix:
Apply in all four places.