mirror of https://github.com/bsnes-emu/bsnes.git
Improved sanitation for save states for better security and stability
This commit is contained in:
parent
8d319c65c2
commit
4498d16bed
|
@ -658,7 +658,7 @@ static const uint8_t workboy_vk_to_key[] = {
|
||||||
|
|
||||||
if ( [[pboard types] containsObject:NSURLPboardType] ) {
|
if ( [[pboard types] containsObject:NSURLPboardType] ) {
|
||||||
NSURL *fileURL = [NSURL URLFromPasteboard:pboard];
|
NSURL *fileURL = [NSURL URLFromPasteboard:pboard];
|
||||||
if (GB_is_stave_state(fileURL.fileSystemRepresentation)) {
|
if (GB_is_save_state(fileURL.fileSystemRepresentation)) {
|
||||||
return NSDragOperationGeneric;
|
return NSDragOperationGeneric;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "cheats.h"
|
#include "cheats.h"
|
||||||
#include "rumble.h"
|
#include "rumble.h"
|
||||||
#include "workboy.h"
|
#include "workboy.h"
|
||||||
|
#include "random.h"
|
||||||
|
|
||||||
#define GB_STRUCT_VERSION 13
|
#define GB_STRUCT_VERSION 13
|
||||||
|
|
||||||
|
@ -554,7 +555,7 @@ struct GB_gameboy_internal_s {
|
||||||
/* Video Display */
|
/* Video Display */
|
||||||
GB_SECTION(video,
|
GB_SECTION(video,
|
||||||
uint32_t vram_size; // Different between CGB and DMG
|
uint32_t vram_size; // Different between CGB and DMG
|
||||||
uint8_t cgb_vram_bank;
|
bool cgb_vram_bank;
|
||||||
uint8_t oam[0xA0];
|
uint8_t oam[0xA0];
|
||||||
uint8_t background_palettes_data[0x40];
|
uint8_t background_palettes_data[0x40];
|
||||||
uint8_t sprite_palettes_data[0x40];
|
uint8_t sprite_palettes_data[0x40];
|
||||||
|
|
|
@ -52,6 +52,7 @@ void GB_update_joyp(GB_gameboy_t *gb)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
__builtin_unreachable();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -291,7 +291,7 @@ static uint8_t read_vram(GB_gameboy_t *gb, uint16_t addr)
|
||||||
addr = gb->last_tile_data_address;
|
addr = gb->last_tile_data_address;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return gb->vram[(addr & 0x1FFF) + (uint16_t) gb->cgb_vram_bank * 0x2000];
|
return gb->vram[(addr & 0x1FFF) + (gb->cgb_vram_bank? 0x2000 : 0)];
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr)
|
static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr)
|
||||||
|
@ -464,7 +464,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned i = 0; i < 8; i++) {
|
for (unsigned i = 0; i < 8; i++) {
|
||||||
|
@ -635,8 +635,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
|
||||||
}
|
}
|
||||||
return 0xFF;
|
return 0xFF;
|
||||||
}
|
}
|
||||||
/* Hardware registers */
|
__builtin_unreachable();
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addr == 0xFFFF) {
|
if (addr == 0xFFFF) {
|
||||||
|
@ -815,7 +814,7 @@ static void write_vram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||||
addr = gb->last_tile_data_address;
|
addr = gb->last_tile_data_address;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gb->vram[(addr & 0x1FFF) + (uint16_t) gb->cgb_vram_bank * 0x2000] = value;
|
gb->vram[(addr & 0x1FFF) + (gb->cgb_vram_bank? 0x2000 : 0)] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool huc3_write(GB_gameboy_t *gb, uint8_t value)
|
static bool huc3_write(GB_gameboy_t *gb, uint8_t value)
|
||||||
|
@ -1395,7 +1394,7 @@ static GB_write_function_t * const write_map[] =
|
||||||
write_vram, write_vram, /* 8XXX, 9XXX */
|
write_vram, write_vram, /* 8XXX, 9XXX */
|
||||||
write_mbc_ram, write_mbc_ram, /* AXXX, BXXX */
|
write_mbc_ram, write_mbc_ram, /* AXXX, BXXX */
|
||||||
write_ram, write_banked_ram, /* CXXX, DXXX */
|
write_ram, write_banked_ram, /* CXXX, DXXX */
|
||||||
write_ram, write_high_memory, /* EXXX FXXX */
|
write_ram, write_high_memory, /* EXXX FXXX */
|
||||||
};
|
};
|
||||||
|
|
||||||
void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||||
|
|
|
@ -307,6 +307,11 @@ static bool verify_and_update_state_compatibility(GB_gameboy_t *gb, GB_gameboy_t
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (GB_is_cgb(gb) != GB_is_cgb(save) || GB_is_hle_sgb(gb) != GB_is_hle_sgb(save)) {
|
||||||
|
GB_log(gb, "The save state is for a different Game Boy model. Try changing the emulated model.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (gb->mbc_ram_size < save->mbc_ram_size) {
|
if (gb->mbc_ram_size < save->mbc_ram_size) {
|
||||||
GB_log(gb, "The save state has non-matching MBC RAM size.\n");
|
GB_log(gb, "The save state has non-matching MBC RAM size.\n");
|
||||||
return false;
|
return false;
|
||||||
|
@ -333,7 +338,26 @@ static bool verify_and_update_state_compatibility(GB_gameboy_t *gb, GB_gameboy_t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
switch (save->model) {
|
||||||
|
case GB_MODEL_DMG_B: return true;
|
||||||
|
case GB_MODEL_SGB_NTSC: return true;
|
||||||
|
case GB_MODEL_SGB_PAL: return true;
|
||||||
|
case GB_MODEL_SGB_NTSC_NO_SFC: return true;
|
||||||
|
case GB_MODEL_SGB_PAL_NO_SFC: return true;
|
||||||
|
case GB_MODEL_MGB: return true;
|
||||||
|
case GB_MODEL_SGB2: return true;
|
||||||
|
case GB_MODEL_SGB2_NO_SFC: return true;
|
||||||
|
case GB_MODEL_CGB_C: return true;
|
||||||
|
case GB_MODEL_CGB_D: return true;
|
||||||
|
case GB_MODEL_CGB_E: return true;
|
||||||
|
case GB_MODEL_AGB: return true;
|
||||||
|
}
|
||||||
|
if ((gb->model & GB_MODEL_FAMILY_MASK) == (save->model & GB_MODEL_FAMILY_MASK)) {
|
||||||
|
save->model = gb->model;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
GB_log(gb, "This save state is for an unknown Game Boy model\n");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sanitize_state(GB_gameboy_t *gb)
|
static void sanitize_state(GB_gameboy_t *gb)
|
||||||
|
@ -347,6 +371,35 @@ static void sanitize_state(GB_gameboy_t *gb)
|
||||||
gb->bg_fifo.write_end &= 0xF;
|
gb->bg_fifo.write_end &= 0xF;
|
||||||
gb->oam_fifo.read_end &= 0xF;
|
gb->oam_fifo.read_end &= 0xF;
|
||||||
gb->oam_fifo.write_end &= 0xF;
|
gb->oam_fifo.write_end &= 0xF;
|
||||||
|
gb->last_tile_index_address &= 0x1FFF;
|
||||||
|
gb->window_tile_x &= 0x1F;
|
||||||
|
|
||||||
|
/* These are kind of DOS-ish if too large */
|
||||||
|
if (abs(gb->display_cycles) > 0x8000) {
|
||||||
|
gb->display_cycles = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (abs(gb->div_cycles) > 0x8000) {
|
||||||
|
gb->div_cycles = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!GB_is_cgb(gb)) {
|
||||||
|
gb->cgb_mode = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gb->ram_size == 0x8000) {
|
||||||
|
gb->cgb_ram_bank &= 0x7;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gb->cgb_ram_bank = 1;
|
||||||
|
}
|
||||||
|
if (gb->vram_size != 0x4000) {
|
||||||
|
gb->cgb_vram_bank = 0;
|
||||||
|
}
|
||||||
|
if (!GB_is_cgb(gb)) {
|
||||||
|
gb->current_tile_attributes = 0;
|
||||||
|
}
|
||||||
|
|
||||||
gb->object_low_line_address &= gb->vram_size & ~1;
|
gb->object_low_line_address &= gb->vram_size & ~1;
|
||||||
if (gb->lcd_x > gb->position_in_line) {
|
if (gb->lcd_x > gb->position_in_line) {
|
||||||
gb->lcd_x = gb->position_in_line;
|
gb->lcd_x = gb->position_in_line;
|
||||||
|
@ -918,7 +971,9 @@ static int load_bess_save(GB_gameboy_t *gb, virtual_file_t *file, bool is_samebo
|
||||||
|
|
||||||
save.halted = core.execution_mode == 1;
|
save.halted = core.execution_mode == 1;
|
||||||
save.stopped = core.execution_mode == 2;
|
save.stopped = core.execution_mode == 2;
|
||||||
|
|
||||||
|
// Done early for compatibility with 0.14.x
|
||||||
|
GB_write_memory(&save, 0xFF00 + GB_IO_SVBK, core.io_registers[GB_IO_SVBK]);
|
||||||
// CPU related
|
// CPU related
|
||||||
|
|
||||||
// Determines DMG mode
|
// Determines DMG mode
|
||||||
|
@ -979,7 +1034,6 @@ static int load_bess_save(GB_gameboy_t *gb, virtual_file_t *file, bool is_samebo
|
||||||
GB_write_memory(&save, 0xFF00 + GB_IO_BGPI, core.io_registers[GB_IO_BGPI]);
|
GB_write_memory(&save, 0xFF00 + GB_IO_BGPI, core.io_registers[GB_IO_BGPI]);
|
||||||
GB_write_memory(&save, 0xFF00 + GB_IO_OBPI, core.io_registers[GB_IO_OBPI]);
|
GB_write_memory(&save, 0xFF00 + GB_IO_OBPI, core.io_registers[GB_IO_OBPI]);
|
||||||
GB_write_memory(&save, 0xFF00 + GB_IO_OPRI, core.io_registers[GB_IO_OPRI]);
|
GB_write_memory(&save, 0xFF00 + GB_IO_OPRI, core.io_registers[GB_IO_OPRI]);
|
||||||
GB_write_memory(&save, 0xFF00 + GB_IO_SVBK, core.io_registers[GB_IO_SVBK]);
|
|
||||||
|
|
||||||
// Interrupts
|
// Interrupts
|
||||||
GB_write_memory(&save, 0xFF00 + GB_IO_IF, core.io_registers[GB_IO_IF]);
|
GB_write_memory(&save, 0xFF00 + GB_IO_IF, core.io_registers[GB_IO_IF]);
|
||||||
|
@ -1018,6 +1072,7 @@ static int load_bess_save(GB_gameboy_t *gb, virtual_file_t *file, bool is_samebo
|
||||||
case BE32('MBC '):
|
case BE32('MBC '):
|
||||||
if (!found_core) goto parse_error;
|
if (!found_core) goto parse_error;
|
||||||
if (LE32(block.size) % 3 != 0) goto parse_error;
|
if (LE32(block.size) % 3 != 0) goto parse_error;
|
||||||
|
if (LE32(block.size) > 0x1000) goto parse_error;
|
||||||
for (unsigned i = LE32(block.size); i > 0; i -= 3) {
|
for (unsigned i = LE32(block.size); i > 0; i -= 3) {
|
||||||
BESS_MBC_pair_t pair;
|
BESS_MBC_pair_t pair;
|
||||||
file->read(file, &pair, sizeof(pair));
|
file->read(file, &pair, sizeof(pair));
|
||||||
|
@ -1165,6 +1220,7 @@ error:
|
||||||
GB_log(gb, "Attempted to import a save state from a different emulator or incompatible version, but the save state is invalid.\n");
|
GB_log(gb, "Attempted to import a save state from a different emulator or incompatible version, but the save state is invalid.\n");
|
||||||
}
|
}
|
||||||
GB_free(&save);
|
GB_free(&save);
|
||||||
|
sanitize_state(gb);
|
||||||
return errno;
|
return errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1272,7 +1328,7 @@ int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t le
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool GB_is_stave_state(const char *path)
|
bool GB_is_save_state(const char *path)
|
||||||
{
|
{
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
FILE *f = fopen(path, "rb");
|
FILE *f = fopen(path, "rb");
|
||||||
|
|
|
@ -27,7 +27,7 @@ void GB_save_state_to_buffer(GB_gameboy_t *gb, uint8_t *buffer);
|
||||||
|
|
||||||
int GB_load_state(GB_gameboy_t *gb, const char *path);
|
int GB_load_state(GB_gameboy_t *gb, const char *path);
|
||||||
int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t length);
|
int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t length);
|
||||||
bool GB_is_stave_state(const char *path);
|
bool GB_is_save_state(const char *path);
|
||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
static inline uint32_t state_magic(void)
|
static inline uint32_t state_magic(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -656,6 +656,7 @@ static bool condition_code(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
case 3:
|
case 3:
|
||||||
return (gb->af & GB_CARRY_FLAG);
|
return (gb->af & GB_CARRY_FLAG);
|
||||||
}
|
}
|
||||||
|
__builtin_unreachable();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1344,7 +1344,7 @@ void run_gui(bool is_running)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDL_DROPFILE: {
|
case SDL_DROPFILE: {
|
||||||
if (GB_is_stave_state(event.drop.file)) {
|
if (GB_is_save_state(event.drop.file)) {
|
||||||
if (GB_is_inited(&gb)) {
|
if (GB_is_inited(&gb)) {
|
||||||
dropped_state_file = event.drop.file;
|
dropped_state_file = event.drop.file;
|
||||||
pending_command = GB_SDL_LOAD_STATE_FROM_FILE_COMMAND;
|
pending_command = GB_SDL_LOAD_STATE_FROM_FILE_COMMAND;
|
||||||
|
|
|
@ -225,7 +225,7 @@ static void handle_events(GB_gameboy_t *gb)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_DROPFILE: {
|
case SDL_DROPFILE: {
|
||||||
if (GB_is_stave_state(event.drop.file)) {
|
if (GB_is_save_state(event.drop.file)) {
|
||||||
dropped_state_file = event.drop.file;
|
dropped_state_file = event.drop.file;
|
||||||
pending_command = GB_SDL_LOAD_STATE_FROM_FILE_COMMAND;
|
pending_command = GB_SDL_LOAD_STATE_FROM_FILE_COMMAND;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue