7939d92b3e930850538ea059159c2d5145c255ca
Pass 9 audit of the iNES, UNIF, and FDS loaders against malformed ROM
input under AddressSanitizer + UndefinedBehaviorSanitizer. The ROM file
is the entry point for untrusted input, so any out-of-bounds read,
write, integer overflow, or DoS in the parsers is a real attack
surface for anyone who downloads ROMs from third-party sites.
Two pre-existing crash inputs caught by ASAN
============================================
test_zero_prg.nes : iNES 2.0 header that decodes to PRGRomSize == 0.
Master loads it: uppow2(0) = 0, FCEU_malloc(0)
returns an implementation-defined small/null
buffer, and the first PRG fetch from the reset
vector reads past the heap.
=> heap-buffer-overflow at cart.c:138 CartBR
test_mapr_huge_unif.unf : UNIF MAPR chunk with declared size 0xFFFFFFFF.
Master computes FCEU_malloc(uchead.info + 1)
which overflows uint32_t to FCEU_malloc(0); the
subsequent fread of 4 GiB then heap-corrupts and
boardname[uchead.info] = 0 finally segfaults.
=> SEGV at unif.c:345 SetBoardName
Plus a UNIF CTRL DoS: a CTRL chunk declaring 4 GiB drives the
diagnostic dump loop through 4 billion FCEU_fgetc() calls (and the
loop didn't even break on EOF), printf'ing each byte to the log.
Pass 9 fuzz inputs reject every one of those cleanly. Bit-exact
identical audio + video output to upstream master across nine valid
test ROMs (silent, idle, active gameplay, 5-channel max-volume stress,
and the per-APU-channel test ROMs).
Changes
=======
src/ines.c (iNESLoad):
* Reject PRGRomSize <= 0. The legacy iNES path already substitutes
ROM_size = 256 when the byte is zero, but the iNES 2.0 exponent
encoding can still resolve to zero from a malformed header.
* Cast PRGRomSize + CHRRomSize to uint64_t before adding. Both fields
are int with a 0x40000000 cap from iNES_read_header_info, so the
addition can reach 0x80000000 - signed overflow (UB) that wraps
negative and then sign-extends bogusly into the uint64_t romSize
used for size sanity prints.
* Check the FCEU_fread return value on every read (trainer, PRG,
CHR, MiscROM) and warn on truncation. The buffers are pre-filled
with 0xFF (open bus) or zero, so a short read leaves a sane tail,
but the user got no warning that the file was incomplete.
src/unif.c:
* FixRomSize: change return type from int to uint32_t. The 0x80000000
cap was being implicitly converted from signed int (where it is
INT_MIN) to the uint64_t UNIF_PRGROMSize/CHRROMSize callers, sign-
extending to 0xFFFFFFFF80000000 - a 16 EiB malloc request.
* SetBoardName: cap uchead.info at 256 (in addition to the existing
< 4 reject). Real UNIF board-name strings are < 32 characters.
Without the upper bound, uchead.info near 0xFFFFFFFF makes
(uchead.info + 1) wrap to zero and FCEU_malloc returns a tiny
buffer that the subsequent 4 GiB fread heap-overflows. Also check
the fread return.
* DoMirroring / CTRL: cap the diagnostic dump-and-skip path at 16
bytes and FCEU_fseek past the remainder. Without the cap, a chunk
declaring info ~ 0xFFFFFFFF would spin 4 billion FCEU_fgetc/printf
iterations - CPU-bound DoS. The CTRL branch also lacked an EOF
break, so a truncated stream would keep returning -1 forever.
src/fds.c (SubLoad):
* Zero-initialise the stack header[16] so partial reads see
deterministic zeros rather than stack garbage. The fsize >= 16
guard reduces the chance of a short read but doesn't eliminate
it (mid-stream IO error etc).
* Check the FCEU_fread return on the header read and on each
65500-byte side read; warn on truncation.
Verification
============
ASAN/UBSAN sweep across 18 fuzz inputs:
* Every case that crashed master now LOAD_REJECTED on pass 9.
* No new UBSAN/ASAN findings introduced (the pre-existing
ppu.c:85 left-shift UB shows up identically on both sides).
Bit-exact audio dump regression vs master across 9 valid ROMs at 600
frames each: identical output. No behavioral change for legitimate
input.
FCE Ultra mappers modified
FCEU "mappers modified" is an unofficial build of FCEU Ultra by CaH4e3, which supports a lot of new mappers including some obscure mappers such as one for unlicensed NES ROM's.
Sequential targets Light Guns support added
Support for Sequential targets Light Guns has been added. "Gun Aux A" serves as light sensor logic input.
Description
Languages
C
97.7%
Python
2%
C++
0.2%