gb: refuse a map header that does not fit wOverworldMap

The buffer is ds 1300 and every real map plus its three-block border fits in it.
A header that says otherwise is one read mid-load, and decoding it would read
past the buffer into somebody else s WRAM, so it is a refusal rather than a
clamp.
This commit is contained in:
claude 2026-09-22 04:15:29 +00:00
parent 44a7011299
commit abd3e563a8
2 changed files with 15 additions and 0 deletions

View file

@ -49,6 +49,13 @@ pub const MAP_BORDER: usize = 3;
/// which is why `wCurMapWidth` in blocks is `map_size().width` in tiles divided by this. /// which is why `wCurMapWidth` in blocks is `map_size().width` in tiles divided by this.
pub const TILES_PER_BLOCK: u8 = 2; pub const TILES_PER_BLOCK: u8 = 2;
/// Bytes `wOverworldMap` has for the loaded map: `ds 1300` (`ram/wram.asm`).
///
/// A map plus its border of [`MAP_BORDER`] blocks has to fit in this, and every real map does. A
/// header that says otherwise is a header read mid-load, which is why the bound is a refusal
/// rather than a clamp.
pub const OVERWORLD_MAP_BYTES: usize = 1300;
/// Which of a quadrant's two rows the collision read takes its tile id from: the lower one. /// Which of a quadrant's two rows the collision read takes its tile id from: the lower one.
/// ///
/// A map tile is a 2x2 patch of screen tiles and only one of the four is ever asked about, because /// A map tile is a 2x2 patch of screen tiles and only one of the four is ever asked about, because

View file

@ -895,6 +895,14 @@ pub fn map_grid(memory: &mut dyn MemoryReader) -> Result<MapGrid, GridRefusal> {
let height_blocks = read(memory, ram::wCurMapHeight); let height_blocks = read(memory, ram::wCurMapHeight);
let stride = u16::from(width_blocks) + (mapgrid::MAP_BORDER as u16) * 2; let stride = u16::from(width_blocks) + (mapgrid::MAP_BORDER as u16) * 2;
let border = mapgrid::MAP_BORDER as u16; let border = mapgrid::MAP_BORDER as u16;
// The map plus its border has to fit in `wOverworldMap`, which every real map does. One that
// does not is a header caught mid-load, and reading past the buffer would be reading somebody
// else's WRAM.
if usize::from(stride) * (usize::from(height_blocks) + mapgrid::MAP_BORDER * 2)
> mapgrid::OVERWORLD_MAP_BYTES
{
return Err(GridRefusal::NoHeader);
}
let mut blocks = Vec::with_capacity(usize::from(width_blocks) * usize::from(height_blocks)); let mut blocks = Vec::with_capacity(usize::from(width_blocks) * usize::from(height_blocks));
for row in 0..u16::from(height_blocks) { for row in 0..u16::from(height_blocks) {
for column in 0..u16::from(width_blocks) { for column in 0..u16::from(width_blocks) {