mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-10 03:21:30 -10:00
Sweep of the Wii/libogc-side code:
Memory safety
- mem2: _mem2_strndup used a calloc-then-memcpy(n+1) sequence that
(a) read past s when strlen(s) < n (UB) and (b) left the result
without a NUL terminator when strlen(s) >= n. Rewritten via
strnlen() with an explicit terminator and _mem2_malloc instead
of _mem2_calloc.
- mem2: _mem2_calloc had no num*size overflow check; large pairs
silently wrapped and produced a tiny allocation that the caller
proceeded to write a huge amount into. Added the standard
UINT32_MAX/size guard.
- mem2: _mem2_memalign silently downgraded any requested alignment
to the heap's 32-byte page size. The lwp_heap can't honour
stricter requests, so reject them (return NULL) rather than lie.
- gx_joypad: gx_joypad_button returned (pad_state[port] & (1ULL<<key))
cast to int32_t, truncating bit >= 32 to zero and silently reporting
"not pressed" for every Wiimote/Classic/Nunchuk binding. Cast to
bool first.
- gx_joypad: gx_joypad_get_buttons used BITS_COPY16_PTR on a uint64_t
pad_state, dropping every Wiimote/Classic/Nunchuk bit (28..44) and
GX_QUIT_KEY (62). Use BITS_COPY64_PTR.
- gx_gfx: menu_tex.data was sized 240*212 uint32_t (203,520 bytes),
but convert_texture16 writes width*height*2 bytes into it and
gx_set_video_mode allows up to 560x240 for the 21:9 RGUI aspect
cases, producing a ~64 KB write past the buffer. Sized for the
worst-case 560*240 explicitly. The DCFlushRange call alongside
passed fb_width*fb_pitch (i.e. fb_width^2 * 2), conflating width
and height; corrected to fb_height*fb_pitch.
Leaks
- platform_gx: frontend_gx_deinit didn't release the device-thread
mutex/cond/mutex triple created in frontend_gx_init. Re-init
cycles (CMD_EVENT_QUIT + relaunch) leaked all three. Freed.
- gx_gfx: g_tex.data is memalign'd in init_vtx and was never freed
by gx_free. Now released alongside the rest of the GX state.
- gx_gfx: both XFB framebuffers in gx->framebuf[] were allocated
via MEM_K0_TO_K1(memalign(...)) in setup_video_mode and never
freed. free() takes the cached alias - use MEM_K1_TO_K0() before
passing.
Correctness
- platform_gx: DEFAULT_DIR_REMAP was filled from DEFAULT_DIR_MENU_CONFIG
*before* MENU_CONFIG itself was filled, producing a remap path
rooted at the empty/stale MENU_CONFIG value on first boot. Swapped.
- gx_joypad: check_port0_active() was called inside the per-port
loop, four times per poll, with a partial pad_count for the first
three iterations - which could force-enable port 0 spuriously.
Hoisted outside the loop.
- gx_input: rvl_input_poll's realloc-grow path zeroed only x_last/y_last
on the (possibly newly-allocated) tail entries, leaving x_abs/y_abs
uninitialised. The next poll's x_last = x_abs picked up uninitialised
heap data, producing a one-frame garbage delta on hot-plug. Zero
exactly the new tail with memset; existing entries keep their
last known position. Also dropped the shadowed inner 'unsigned i;'
and the redundant 'gx &&' on the inner if.
- gx_input: lightgun path had a video_driver_get_viewport_info(&vp)
call immediately followed by six lines that zeroed every field it
populated. The dispatcher went through the video vtable on every
lightgun query for nothing - removed.
- gx_audio: gx_audio_data was 'static volatile gx_audio_t *', making
every member access through the pointer volatile, including the
4 KB sample data[][] array (which is hot). The three IRQ-shared
fields are individually volatile-qualified in the struct, which is
sufficient. Dropped the outer volatile and the gratuitous cast in
the DMA callback. Also published gx_audio_data only after the
struct is fully initialised, so the IRQ never sees a partial state.
Folds / cleanup
- mem2: all _mem2_* helpers now static - they are only called from
the __wrap_* functions in the same TU (Makefile.wii uses
-Wl,-wrap,malloc,...). Lets the compiler inline them aggressively.
- mem2: _mem2_strdup and _mem2_strndup were using _mem2_calloc and
then immediately memcpy'ing over the zeroed bytes. _mem2_malloc
is enough.
- mem2: _CPU_ISR_Disable's "level" output was being silenced with
a (void)level; cast in __lwp_heap_block_size. Replaced with the
same `uint32_t level = 0;` idiom the rest of the codebase uses.
- platform_gx: frontend_gx_get_free_mem computed
SYSMEM1_SIZE - (SYSMEM1_SIZE - SYS_GetArena1Size())
which is just SYS_GetArena1Size(). Compiler folds it, but the
source read as if there was something subtle going on. Reduced.
- platform_gx: gx_rom_path is `extern char[]`. The `gx_rom_path &&`
arm of the if-condition is a tautology - removed.
- platform_gx: the two FRONTEND_FORK_CORE / FRONTEND_FORK_CORE_WITH_ARGS
switch arms had identical bodies - folded.
- platform_gx: device-name buffer build used two strlcpy()s; one
snprintf is the same cost and one less line.
- app_booter: two-statement (p_paddr &= 0x3FFFFFFF; p_paddr |= 0x80000000;)
bit fold; direct-init for phdrs and dolfile; collapsed
app_booter_main's exeEntryPointAddress to a conditional initialiser.
- gx_gfx: verts[], vertex_ptr[], color_ptr[], menu_gx_resolutions[]
had external linkage but were referenced only within gx_gfx.c, and
none were written. Marked static const so they go in .rodata. Call
sites to GX_SetArray (which takes void*) get explicit casts.
- gx_gfx: gx_blit_line had several declare-then-assign locals derived
from gx-> and gx_mode; folded into direct const initialisers, with
the gx NULL-check moved above the derefs.
- gx_audio: gx_audio_copy_swapped (which is not an endianness swap,
it's a 16/16 word rotate for the AI hardware's L/R lane order)
renamed to gx_audio_lr_swap and unrolled by 4 for better dispatch
on Broadway/Gekko.