Commit Graph

269 Commits

Author SHA1 Message Date
U-DESKTOP-SPFP6AQ\twistedtechre
766f84662b core: stdint typedefs, LE optimizations, frame determinism
Three follow-up audit passes on top of the memory-safety / leak /
savestate-portability work in 1185db8.

==============================================================
Pass 1: replace custom typedefs with C99 stdint types throughout
==============================================================

The custom uint8 / uint16 / uint32 / uint64 / int8 / int16 / int32 /
int64 typedefs in src/fceu-types.h were just simple aliases for the
C99 stdint.h types. Replace them with the standard names directly.

  - 498 files modified
  - ~3,400 token replacements (uint8 -> uint8_t, etc)
  - fceu-types.h slimmed down to just INLINE / GINLINE / FASTAPASS
    macros and the readfunc / writefunc function-pointer typedefs
    (those now use uint8_t / uint32_t natively)
  - Build clean on `make platform=unix` with zero new warnings
  - Output binary size unchanged - confirming semantic equivalence

Mechanical replacement done with a Python script that uses word-
boundary regex to avoid false positives (e.g. 'uint32_t' was
correctly left alone because '_' is a word character so 'uint32'
is not a complete word inside it).

================================================================
Pass 2: prefer memcpy on LE hosts for endian read/write helpers
================================================================

fceu-endian.c's write32le_mem, FCEU_en32lsb, and FCEU_de32lsb
performed bytewise composition/decomposition unconditionally. On
LE hosts the in-memory representation already matches the desired
LE on-disk format, so a single memcpy is equivalent and lets the
compiler emit a single load/store rather than four byte ops.

  - The bytewise path is kept inside #ifdef MSB_FIRST for BE hosts
    where it implements the actual byte swap
  - Both forms produce identical results; this is a code-clarity
    change more than a performance one (the optimizer was already
    merging the shifts on LE), but it documents the intent and
    removes a strict-aliasing-flavoured cast through
    *(uint32_t*)Bufo
  - Added missing #include <string.h> in fceu-endian.c which was
    relying on transitive includes for memcpy

Other MSB_FIRST sites in the codebase (state.c FlipByteOrder
guards, ppu.c sprite-line rendering, boards/unrom512.c flash-write-
counter access) were already optimized for LE; they were verified
correct rather than changed.

================================================================
Pass 3: frame determinism for replay and netplay
================================================================

Two libc rand() sites in core were replaced with a local xorshift32
PRNG so that NES games which read uninitialised memory or hit
hardware "weak bit" emulation produce reproducible behaviour across
runs. NES titles routinely read uninitialised RAM (variables not
zeroed before use, sprite Y-position set by junk-on-stack), so the
RAM contents at power-on subtly affect game behaviour. With libc
rand(), those contents depend on whether anyone else seeded rand()
in the same process - a different libretro frontend, a different
audio backend init order, or any frontend that does srand(time(0))
all break replay / netplay frame-determinism.

1. fceu.c FCEU_MemoryRand. Used to fill RAM (PowerNES) and CHR-RAM
   (iNES_Init) at power-on when option_ramstate=2 (random init).
   Replaced with a local xorshift32 PRNG, exposed via a new
   FCEU_MemoryRand_Reseed(uint32_t) function called once per
   power-on:
   - PowerNES seeds from the first 4 bytes of GameInfo->MD5 (set
     by all loaders before PowerNES runs) so identical ROMs
     produce identical RAM, different ROMs differ
   - iNES_Init seeds from iNESCart.PRGCRC32 before the CHR-RAM
     fill so two builds of the same ROM get the same CHR-RAM
   - The PRNG state advances across multiple FCEU_MemoryRand
     calls within one power-on so RAM and CHR-RAM get different
     content (matching NES hardware reality)

2. boards/rt-01.c UNLRT01Read. The RT-01 board has 'weak bit'
   protected EPROM regions; reads of 0xCE80-0xCEFF and 0xFE80-
   0xFEFF return 0xF2 with the low 3 bits randomised. Replaced
   libc rand() with a local xorshift32 seeded at power-on, and
   added the PRNG state to the savestate via AddExState with key
   "WBKS" so save / load / rewind / netplay rollback all stay
   deterministic.

In addition, two long-double-to-int truncations were changed to
double for cross-platform FP determinism:

  - sound.c SetSoundVariables: soundtsinc
  - boards/n106.c DoNamcoSound: inc

long double has platform-dependent precision (80-bit on x87,
64-bit with -mfpmath=sse, 128-bit on PowerPC), so the truncated
integer result varied across these platforms. double is
guaranteed 64-bit IEEE-754 portably.

After this pass, the core has no time(), clock(), gettimeofday(),
clock_gettime(), getpid(), getuid(), getgid(), getenv(), gethostid(),
pthread, std::thread, OpenMP, signal handler, or non-deterministic-
malloc dependency. Verified with a Python scanner that greps the
source for these patterns; runs clean.

The PPU / APU / CPU power-on already explicitly memset all state
buffers to 0 (deterministic), and ROM/CHR-ROM allocation already
memsets to 0xFF before partial fread (deterministic regardless of
file truncation).

Combined with the memory-safety hardening in 1185db8 (which
prevents savestate-loaded indices from going out-of-bounds and
producing unpredictable behaviour), the core now offers genuine
frame-deterministic replay across runs, builds, and host endian.
2026-05-04 02:46:34 +02:00
U-DESKTOP-SPFP6AQ\twistedtechre
1185db89c1 core: memory-safety, leak, and savestate-portability audit fixes
Squashed series of ~50 distinct bugs found during a multi-day
security/correctness audit, ranging from ROM-triggerable heap
corruption and savestate-triggered OOB read primitives down to
obscure cross-platform savestate breakage.

Build is clean on `make platform=unix` with zero new warnings.

CRITICAL (ROM-triggerable, exploitable on the user's machine)
=============================================================

* ines.c iNES 2.0 PRG/CHR exponent overflow leading to undersized
  allocation followed by heap buffer overflow on fread (pow() with
  attacker-chosen exponent up to 63). Cap exponent at 30 and
  compute size in uint32 with explicit cap below 2 GiB before
  storing in int.
* ines.c miscROMSize int wraparound from attacker-controlled PRG/
  CHR sizes; previous '& 0x8000000' check missed common underflow
  cases. Compute in int64 and reject <= 0 or > 128 MiB.
* unif.c MAPR chunk OOB heap read on chunks of size < 4. Validate
  chunk size before allocating board-name buffer.
* unif.c chunk size truncation: int conversion + missing cap allowed
  a 4 GiB chunk size to wrap. Cap at 16 MiB.
* unif.c FixRomSize infinite loop on size > 0x80000000. Cap input.
* unif.c DINF chunk: months[(m - 1) % 12] is UB for m==0; m comes
  from the .unf file. Clamp m into [1..12] before subtracting.
* unif.c NAME chunk: GameInfo->name = malloc(...) was unchecked.
* nsf.c size underflow: FCEU_fgetsize() - 0x80 wraps to ~UINT64_MAX
  on tiny files, propagating a huge size into uppow2/FCEU_malloc.
  Validate size > 0x80 before subtracting.
* nsf.c NSFMaxBank * 4096 cap tightened from 1<<20 (UB on signed-int
  overflow) to 1<<19 (fits in int).
* general.c uppow2 had signed-int UB (1 << 32) and wrapped to 0 for
  inputs near uint32 max. Cap at 0x80000000.
* cheat.c SubCheats[256] BSS overflow when more than 256 GG/PAR
  substitution cheats are active. The array is adjacent to MMapPtrs[64]
  in BSS which is exposed via retro_get_memory_data, making this
  overflow visible from outside the core.
* libretro.c retro_cheat_set strcpy stack overflow with arbitrary-
  length cheat strings from the frontend. Use strlcpy.

CRITICAL (savestate-triggerable on a malicious .fcs file)
=========================================================

* fds.c FDS savestate OOB read primitive: InDisk loaded from
  savestate is used as index into 8-element diskdata[] static
  pointer array. A value 8..254 dereferences arbitrary memory as
  a pointer (heap-read primitive). Also bounds-checked SelectDisk,
  mapperFDS_block, mapperFDS_blockstart, mapperFDS_diskaddr,
  mapperFDS_blocklen so blockstart+diskaddr stays within the 65500-
  byte disk buffer.
* 11 mappers had savestate-loaded variables masked at write time
  but not at restore time, used as indices into fixed arrays:
    - 88.c, KS7037.c, 112.c   cmd indexes reg[8]
    - sachen.c (S8259, S74LS374N) cmd indexes latch[8]
    - 357.c    dipswitch indexes outer_bank[4]
    - unrom512.c flash_state indexes erase_a/d/b[5]
    - 368.c    preg indexes banks[8]
    - 69.c     sndcmd indexes sreg[14]
    - mmc2and4.c latch0/latch1 index creg[4]
  None individually exploitable into RCE - the array writes corrupt
  adjacent BSS with constrained data flowing in - but each is an
  out-of-bounds read or write from attacker-controllable input.

HIGH (memory-safety, reachable on any load)
===========================================

* fceu-memory.c FCEU_malloc deref-of-NULL on allocation failure
  ('ret = 0; memset(ret, 0, size);').
* file.c multiple FCEUFILE/MakeMemWrap/MakeMemWrapBuffer unchecked
  allocations and unchecked filestream_tell return.
* libretro.c GameInfo NULL-derefs in three entry points
  (retro_set_controller_port_device, retro_get_memory_data,
  retro_get_memory_size) reachable on operations called before a
  successful load.
* libretro.c framebuffer leak ~256 KB per failed FCEUI_LoadGame
  (the libretro frontend doesn't call retro_unload_game on a
  failed load).
* libretro.c 3DS retro_deinit unchecked linearFree.
* libretro.c stereo / NTSC filter unchecked malloc returns.
* fceu.c FCEUI_LoadGame unchecked GameInfo malloc.
* fds.c SubLoad and FDSLoad unchecked FCEU_malloc on diskdatao
  backup buffers (NULL-deref in subsequent memcpy).
* fceu.c AllocGenieRW partial-failure leak: AReadG allocated but
  BWriteG malloc fails -> 256 KB leak per retry.
* input/bworld.c Update(): unbounded strcpy from attacker-supplied
  data into 20-byte bdata. Replaced with length-bounded copy.
* cart.c setprg2r/4r and setchr1r/2r/4r/8r mask-underflow guards.
  SetupCartPRGMapping/SetupCartCHRMapping compute (size >> N) - 1
  which underflows to 0xFFFFFFFF for chips smaller than the unit.
  setprg8r/16r/32r and setchr8r already had defensive size checks;
  the smaller units did not. malee.c (2 KB chip) and mapper 218
  (2 KB NTARAM-as-CHR) accidentally avoid OOB; the primitive is
  unsafe for any future board.
* video.c FCEU_InitVirtualVideo XBuf/XDBuf partial-failure leak.
* video.c, fceu.c FCEU_DispMessage / FCEU_printf / FCEU_PrintError:
  vsprintf into fixed stack buffers replaced with vsnprintf.
* core: validate magic-string read length on FDS/UNIF/NSF load
  (reject files too short to contain the magic so previous static
  buffer / stack garbage doesn't spuriously match).

LEAKS (every cart load/unload cycle)
====================================

* mmc5.c MMC5 cart-side: WRAM (up to 64K) + MMC5fill (1K) + ExRAM
  (1K) leaked per cycle. NSFMMC5_Close existed but was only used
  for NSF code path.
* onebus.c Mapper 270/436: 8 KB CHRRAM leaked per cycle.
* 330.c, 375.c, 528.c: 8 KB WRAM each, no Close at all.

CORRECTNESS (UB / ABI / endianness)
===================================

* fceu-endian.c FlipByteOrder loop bound was 'count' instead of
  'count/2', making it a no-op for every even count and silently
  breaking savestate portability for every FCEUSTATE_RLSB-marked
  field. The '#ifndef GEKKO' workarounds scattered through the
  codebase (sound.c, vrc7.c, vrc6.c) were symptoms of this root
  cause.
* state.c AddExState bounds check ran AFTER writing the entry, so
  when SFEXINDEX hit the 63-entry cap the next call would write
  the entry then immediately overwrite it with the terminator.
* state.c FCEUSS_Save_Mem: post-save callback was guarded by
  'if (SPreSave)' instead of 'if (SPostSave)'.
* Sequence-point UB in counter expressions across 4 board files,
  e.g. 'x = !x ? a : --x' and 'x = ++x % n'. GCC -Wsequence-point
  flags all five sites (285.c, 413.c, asic_mmc3.c, jyasic.c).
* SFORMAT size mismatches between declared variable type and
  AddExState size argument:
    - asic_vrc3.c VRC3_count/VRC3_reload (uint16, saved as 1 byte)
    - mmc3.c m555_count (uint32, saved as 2 bytes), m555_count_-
      expired (uint8, saved as 2 bytes - OOB write into adjacent
      BSS).
* unrom512.c UNROM-512 mapper 30 .srm save format: host-endian
  uint32 flash write counters. Now stored as LE on disk regardless
  of host (Battle Kid 2, Twin Dragons, Lizard, Sole, etc.).
* ~70 multi-byte single-variable SFORMAT/AddExState entries across
  36 board files were saved as host-byte-order. After the
  FlipByteOrder fix, these are now byte-swapped on BE so cross-
  platform savestates round-trip correctly.
* coolgirl.c new ExStateLE() macro added; 22 multi-byte sites.
* pic16c5x.c 11 multi-byte AddExState calls (m_PC, m_PREVPC,
  m_CONFIG, m_WDT, m_prescaler, m_opcode, m_STACK[0/1], m_icount,
  m_delay_timer, m_rtcc, m_inst_cycles, m_clock2cycle).
* onebus.c PowerJoy Supermax submapper detection used non-portable
  *(uint32*)&info->MD5 cast that read different bytes on LE vs BE.
* fds.c clean up redundant FCEUSTATE_RLSB encoding in AddExState
  calls that also passed type=1 (idempotent OR; readability fix).

DOCUMENTATION
=============

* input.c UpdateGP's *(uint32*)data cast looks like a typical
  endian bug but is actually correct (the libretro frontend builds
  JSReturn with matching host-uint32 shifts). Comment added to
  prevent future "fixes" from breaking it.

Limitations not addressed
=========================

* Element-stride-aware byte swapping. The savestate byte-swap
  mechanism (FlipByteOrder over the entire SFORMAT entry buffer)
  is structurally wrong for arrays of multi-byte values: it
  reverses the whole buffer end-to-end instead of byte-swapping
  each element. Several places that need cross-platform-portable
  arrays (VRC7 sound state, jyasic chr[8]) work around this by
  either splitting arrays into per-element SFORMAT entries (n106
  PlayIndex, bandai reg) or by skipping save entirely on BE via
  #ifndef GEKKO. A proper fix would extend the size encoding with
  an element-stride field. Left for a future change because it
  would change the savestate format.

* Strict-aliasing UB in ppu.c (around 9 sites doing
  *(uint32*)uint8_buf for fast 4-byte writes via FCEU_dwmemset).
  Works in practice with all common compilers because the patterns
  are byte-symmetric, but is formally UB.

* FCEU_gmalloc calls exit(1) on OOM. A libretro core should never
  exit() because that takes down the whole frontend. Used by
  100+ call sites; refactoring to return-NULL is out of scope here.

Testing
=======

* Build: clean on `make platform=unix` with -O2; no new warnings.
* FlipByteOrder fix verified by hand-trace and a standalone unit
  test for counts 2, 4, 8.
* uppow2 fix verified by unit test across 13 boundary cases.
* SFORMAT size mismatches and missing-RLSB cases identified by
  Python static-analysis scripts that cross-reference SFORMAT
  entries against variable declarations.
* iNES 2.0 exponent fix verified by hand-tracing what byte 0xFF
  produces post-fix: exp=30 (capped), mult=7, size=3 GiB nominal,
  capped to 1 GiB, capped to 2 GiB by uppow2, FCEU_malloc returns
  NULL on most systems, loader returns 0. No heap overflow for
  any input byte.
* Savestate-loaded array index audit: built a Python scanner that
  extracts each AddExState/SFORMAT entry and cross-references the
  variable name against array-index uses in the same file. All
  flagged sites covered.
* A libFuzzer harness and seed corpus generator (fuzz_main.c,
  gen_seed_corpus.py) accompany this submission for ongoing
  regression testing.
2026-05-04 02:15:40 +02:00
NewRisingSun
29a0caf61b Add keyboard input. 2025-09-13 11:27:38 +02:00
negativeExponent
033c5e2f32 Support for 32bit color format 2025-09-03 16:20:50 +02:00
libretroadmin
3544ff567e Revert "Adjust turbo logic so that when a button gets pressed, it delays this state for 2 frames. Fix some games that requires certain frame count to read button states (Double Dragon, Xevious"
This reverts commit 0676a2092a.
2025-05-02 00:19:38 +02:00
saulfabreg Wii VC Project
cc4aa99507 Add Restored Wii VC palette, original "Nintendo VC" palette renamed to "Wii Virtual Console" (SuperrSonic)
To avoid confusion between different Nintendo's NES color palettes (since the 3DS Virtual Console has a very different NES color palette), the palette "Nintendo Virtual Console" was renamed to "Wii Virtual Console", as that palette mimicks the dark color palette used by the Wii VC.

Also added a "Restored Wii VC" palette which increases the brightness of the Wii VC NES palette to about 30%.

Credit to SuperrSonic for both palettes.
2025-04-23 19:36:09 -07:00
negativeExponent
0676a2092a Adjust turbo logic so that when a button gets pressed, it delays this state for 2 frames. Fix some games that requires certain frame count to read button states (Double Dragon, Xevious 2025-02-06 02:10:32 +01:00
negativeExponent
f72dac45e8 Add support for 512 palette, new deepmhasis method
- Increase palette table from 256 to 1024. the 1st 256 colors are used
  by FCEUmm internally and for compatibility (PS2/PSP will use the old
  emphasis located in this area). New 512 colors with emphasis will be
  read from offset 256 of this palette table.
- Support loading of 512 palette, this will still use the same nes.pal
  filename.
2024-10-16 00:15:30 -05:00
negativeExponent
ac798e1ba4 use 512-palette table for emphasis 2024-10-16 00:15:30 -05:00
libretroadmin
d473fd960f Attempt to fix build for PS2 2024-05-22 15:02:37 +02:00
libretroadmin
40969671ce Add new palettes 2024-03-02 14:26:27 +01:00
StormedBubbles
cd302c0d06 Support Family Trainer 2023-09-15 20:50:27 -04:00
libretroadmin
049b341aa1 Fix C89 compliance 2023-05-03 20:27:46 +02:00
arpruss
977d7f4d03 separate Arkanoid from Zapper code; add mouse sensitivity 2023-03-11 11:40:58 -06:00
tacoschip
3469b5277c add more overscan crop options 2023-03-10 21:47:35 -05:00
arpruss
c63331350d fix Power Pad A 2023-01-07 11:07:14 -06:00
libretroadmin
9fc9458691 Fix C89 build error 2023-01-05 02:10:01 +01:00
arpruss
f473a2a346 fix UI Power Pad spacing 2023-01-01 08:09:19 -06:00
arpruss
d266ba2e20 allow for two controllers to add up to a single PowerPad 2023-01-01 07:30:56 -06:00
arpruss
4dfadfd27a support PowerPad via keyboard qwer/asdf/zxcv 2022-12-31 21:27:46 -06:00
LibretroAdmin
a821de094d Remove unused variables/functions 2022-09-05 01:33:27 +02:00
Carlos O'Connor
1dda0a0705 Add sequential targets light gun support
Support for Sequential targets Light Guns has been added. "Gun Aux A" serves as light sensor logic input.
2022-09-01 02:25:41 -05:00
libretroadmin
e9eaedfb64 Reduce strlens 2022-07-21 17:25:38 +02:00
negativeExponent
11c1793444 Add Firebrandx palettes 2022-06-09 14:04:03 +08:00
jdgleaver
6173b5c71c Add optional 'fake' stereo sound effect 2022-04-06 13:45:22 +01:00
jdgleaver
046637ba73 Expose internal audio RF filter option 2022-04-05 13:45:20 +01:00
Davis Mosenkovs
ae42152216 Add A+B and Turbo A+B buttons 2021-12-23 11:02:55 +02:00
DisasterMo
cb5481af1f Add automatic Crowdin synchronization 2021-12-04 16:43:54 +01:00
jdgleaver
3a1dcca472 Enable switching of colour palettes via RetroPad L2 + D-Pad Left/Right 2021-10-22 13:30:27 +01:00
jdgleaver
1a961ff6be Add support for emulated Game Genie add-on 2021-10-21 15:52:40 +01:00
jdgleaver
499f1c04de Replace direct file access with VFS routines 2021-10-20 16:58:35 +01:00
jdgleaver
0c82e4c73e Update core options to v2 format (add category support) 2021-10-19 17:39:00 +01:00
negativeExponent
4b9f65e27b Fix controller info index 2021-07-04 22:10:50 +08:00
twinaphex
2b187732ea Get rid of obsolete variable 2021-06-05 21:11:41 +02:00
twinaphex
3dbf430262 Wrap all fopen access - go through FCEUD_UTF8fopen isntead 2021-06-05 21:07:55 +02:00
twinaphex
e154706586 Some cleanups + we don't need a non-libretro codepath 2021-06-05 20:56:49 +02:00
Autechre
aa4615c417 Revert "some cleanups" 2021-06-05 15:05:07 +02:00
negativeExponent
6f8be4cedd Simplify palette core options
Fetch names from palette struct instead.
2021-06-05 08:26:45 +08:00
negativeExponent
e75d6be00b Remove ununsed, commented code 2021-06-03 13:30:16 +08:00
negativeExponent
f6cc87a8fa Move overclock-related variables to ppu instead
Just makes more sense since overclocking is ppu-based.
2021-06-03 10:50:30 +08:00
negativeExponent
46ca02a206 Move settings-related vars to FSettings struct
- Just some more cleanups
2021-06-02 04:18:53 +08:00
negativeExponent
403f682eb7 Cleanup: Remove a few extern 2021-05-31 16:55:09 +08:00
tim942
e14c20b02a Update Aspect Ratio
Updated aspect ratio selection from a boolean to a float with a Pixel Perfect option
2021-04-28 18:15:10 -05:00
negativeExponent
61c2d95dac Add support for Konami Hyper Shot
This is just a quick hook-up of the konami hyper shot controller.

-The Konami Hyper Shot is used (and required for gameplay) in the following games:

* Hyper Olympic (J) Konami (1985)
* Hyper Sports (J) Konami (1985)

Fix #439
2021-04-10 15:20:57 +08:00
negativeExponent
8fa62114d8 Apply region overrides when loading new rom (Fix#430) 2021-03-01 06:33:17 +08:00
Francisco Javier Trujillo Mata
c85ab05800 Fix wrong aligment in the buffer 2021-01-14 21:56:40 +01:00
Autechre
0e173120c3 Merge pull request #399 from negativeExponent/updates
misc updates
2020-10-27 16:48:04 +01:00
Alcaro
1258eb759f Don't set pixel_format in retro_init
libretro.h says "This function should be called inside retro_load_game() or retro_get_system_av_info().", let's do as it says.
2020-10-27 04:00:53 +01:00
negativeExponent
4e2c5468e4 Overclock: Remove this for region change
These variables, normal_scanlines and totalscanlines will get updated if
FCUI_SetVidSystem(), so no need to set it here.
2020-10-26 06:36:10 +08:00
negativeExponent
565f60c0f2 Fix some warnings 2020-10-25 08:31:41 +08:00