fix(tile): cap zoom in raster terrarium/mapbox handlers - #527
Conversation
There was a problem hiding this comment.
Pull request overview
This PR mitigates a panic-based denial-of-service vector in the tile service’s raster terrain endpoints by capping invalid zoom requests early and adding defense-in-depth bounds clamping in the upsample path.
Changes:
- Reject
/terrarium/...and/mapbox/...requests withz > terrain.max_zoomwith a 404 before proceeding to tile math. - Harden
extract_and_upsampleby clampingzoom_diffandsub_x/sub_yto prevent out-of-bounds indexing on the parent tile. - Add regression tests intended to ensure out-of-range upsample inputs do not panic.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
tile/src/terrain/mod.rs |
Adds clamping in extract_and_upsample and new regression tests for previously panicking inputs. |
tile/src/server/terrain.rs |
Adds an early zoom cap in raster_tile to reject out-of-range zoom requests with 404. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
CI note (
|
029fb2f to
bb6bc4a
Compare
bb6bc4a to
af8b3df
Compare
Summary
raster_tile(backing/terrarium/{z}/{x}/{y}.{png|webp|avif}and/mapbox/...) never checkedzagainstterrain.max_zoombefore running the parent-tile upsample path inextract_and_upsample.For
z >= dem_max + log2(tile_size) + 1(>= 27 with the shipping defaults),factor > tile_size, soextract_and_upsamplecomputesrow = off_y * tile_sizeup to ~511*256 and indexesparent(lentile_size**2= 65536) far out of bounds. The always-on slice bounds check panics (bounds checks are kept in release too), the panic aborts the request task, the connection resets, and error logs flood — a cheap, unauthenticated DoS on a public endpoint. Legitimate MapLibre clients cap requests atmaxzoom(terrain.max_zoom, default 18) so no real client hits this.Fix
raster_tilehandler: rejectz > terrain.max_zoomwith 404 before any coordinate math. Returns the same shape as the existing "Out of geoid coverage" case.extract_and_upsample(defense-in-depth): saturating clamp onzoom_diffsofactor <= tile_size, and onsub_x/sub_yso the sub-region always stays withinparent. Keeps the panic path closed even if a future caller forgets the upstream guard.Test plan
cargo fmt --checkupsample_zoom_diff_beyond_tile_size_does_not_panic— feedszoom_diff = 10withfactor = 1024 >> tile_size = 4andsub_x/sub_y = 1023(the exact shape of the OOB from az = 27request). Previously panicked; now returns a validtile_size * tile_sizetile.upsample_clamps_sub_x_y_at_boundary— sanity check at the largest safezoom_diff.upsample_extracts_correct_quadrantandupsample_factor_one_is_passthroughstill pass (clamps are no-ops in the legitimate range).cargo check— sandbox has a preexistingobject_storeversion mismatch inpmtiles.rsfiles unrelated to this change; CI resolves againstCargo.lockand is the source of truth.