Skip to content

Out-of-bounds write in UPS patch code #1035

@qurbat

Description

@qurbat

A crafted UPS patch file can write beyond the bounds of Memory.ROM because the relative offset used during patch application is never validated against the output ROM size.

In memmap.cpp:3644-3652, the patch application loop accumulates a relative offset from values decoded from the patch data, then uses it to XOR bytes directly into Memory.ROM:

  uint32 relative = 0;
  while(addr < size - 12) {
      relative += XPSdecode(data, addr, size);
      while(addr < size - 12) {
          uint8 x = data[addr++];
          Memory.ROM[relative++] ^= x;  // <-- no bounds check
          if(!x) break;
      }
  }

While the function checks that out_size <= CMemory::MAX_ROM_SIZE at line 3636, it never validates that relative stays within out_size (or MAX_ROM_SIZE) during the XOR loop. A malicious UPS file can encode arbitrarily large offset values via XPSdecode, causing relative to exceed the allocated buffer and write out of bounds.

An attacker-crafted .ups file can trigger a heap-based out-of-bounds write on Memory.ROM, potentially leading to code execution or a crash. UPS patches are loaded by default when opening a ROM. The patching logic in memmap.cpp:3942 automatically searches for a .ups file matching the ROM filename and applies it without prompting the user. This means simply placing a malicious .ups file alongside a ROM (e.g., in a downloaded ROM pack or shared directory) is sufficient to trigger the vulnerability.

Reproduction: Create a UPS file with a valid UPS1 header and matching patch CRC32, but encode offset values in the patch body that cause the relative offset to exceed MAX_ROM_SIZE. I have also attached a ROM and patch file which will trigger a crash on 32 and 64 bit Windows builds of SNES9x.

Fix: Add a bounds check before the XOR write:

  if (relative >= CMemory::MAX_ROM_SIZE) break;
  Memory.ROM[relative++] ^= x;

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions