diff --git a/components/nes/nofrendo/cpu/nes6502.c b/components/nes/nofrendo/cpu/nes6502.c index 3426013e..887176aa 100644 --- a/components/nes/nofrendo/cpu/nes6502.c +++ b/components/nes/nofrendo/cpu/nes6502.c @@ -1283,6 +1283,17 @@ void nes6502_setcontext(nes6502_context *context) stack = ram + STACK_OFFSET; } +/* Drop cached pointers into shared memory. The dead page is allocated once via +** _my_malloc() and cached in null_page; when the shared memory pool is freed +** (NES teardown) that pointer dangles, so it must be nulled here or the next +** load reuses freed memory (use-after-free -> heap corruption). */ +void nes6502_release_memory(void) +{ + null_page = NULL; + ram = NULL; + stack = NULL; +} + /* get the current context */ void nes6502_getcontext(nes6502_context *context) { diff --git a/components/nes/nofrendo/nes/nes_ppu.c b/components/nes/nofrendo/nes/nes_ppu.c index a5f33f14..2a787b40 100644 --- a/components/nes/nofrendo/nes/nes_ppu.c +++ b/components/nes/nofrendo/nes/nes_ppu.c @@ -60,6 +60,15 @@ void ppu_displaysprites(bool display) ppu->drawsprites = display; } +/* Drop the cached PPU working context. ppu is allocated once via _my_malloc() +** in ppu_setcontext(); when shared memory is freed (NES teardown) that pointer +** dangles, so null it here or the next load writes through a freed pointer +** (use-after-free -> heap corruption). */ +void ppu_release_memory(void) +{ + ppu = NULL; +} + void ppu_setcontext(ppu_t *src_ppu) { int nametab[4]; diff --git a/components/nes/src/nes_shared_memory.c b/components/nes/src/nes_shared_memory.c index b6eabc08..37e7ce32 100644 --- a/components/nes/src/nes_shared_memory.c +++ b/components/nes/src/nes_shared_memory.c @@ -51,6 +51,16 @@ void nes_init_shared_memory(void) { nes_cpu = (nes6502_context *)_my_malloc(sizeof(nes6502_context)); } +// nofrendo caches working buffers in file-static pointers that are allocated +// once via _my_malloc() (the 6502 dead page; the PPU working context). Those +// live in the shared pool that shared_mem_clear() is about to free, so we must +// drop the cached pointers or the next NES load reuses freed memory +// (use-after-free -> heap corruption). +extern void nes6502_release_memory(void); +extern void ppu_release_memory(void); + void nes_free_shared_memory(void) { + nes6502_release_memory(); + ppu_release_memory(); shared_mem_clear(); }