Skip to content

fix: use callback() for missing raster-dem tiles to prevent segfault#2139

Open
JeremyBYU wants to merge 1 commit into
maptiler:masterfrom
JeremyBYU:fix/raster-dem-empty-tile-segfault
Open

fix: use callback() for missing raster-dem tiles to prevent segfault#2139
JeremyBYU wants to merge 1 commit into
maptiler:masterfrom
JeremyBYU:fix/raster-dem-empty-tile-segfault

Conversation

@JeremyBYU

Copy link
Copy Markdown

Summary

When the internal renderer (serve_rendered.js) requests a tile from a local mbtiles/pmtiles source and the tile doesn't exist, createEmptyResponse() generates a 1×1 pixel PNG. For raster-dem sources, this 1×1 PNG is passed to maplibre-native as valid DEM tile data. When DEMData::backfillBorder() later runs between this tile and a real 256×256 neighbor, the dimension mismatch causes an out-of-bounds memory read — segfault.

This PR adds sourceInfo.type === 'raster-dem' to the existing sparse check so that missing DEM tiles always take the callback() (no-content) path, regardless of the sparse flag.

What changed

One condition added in src/serve_rendered.js:

-                if (sparse) {
+                if (sparse || sourceInfo.type === 'raster-dem') {

callback() with zero arguments is maplibre-native's designed "no content" path:

  1. TileLoadersetData(nullptr)
  2. raster_dem_tile_worker.cpp → skips image decoding
  3. raster_dem_tile.cpprenderable = false
  4. render_raster_dem_source.cppisRenderable() returns false → backfill loop skipped

Why only raster-dem?

createEmptyResponse respects sourceInfo.color — a source can specify what color empty tiles should be. Changing this for non-DEM sources would be a breaking change. For raster-dem sources, sourceInfo.color is meaningless (DEM tiles are elevation data, not visual), so callback() is strictly correct.

Risk

Minimal. Since v5.4.0, sparse defaults to true, so the vast majority of raster-dem sources already hit the callback() path. This change only affects the edge case where sparse is explicitly set to false on a raster-dem source — which can cause a crash depending on memory layout and neighbor tile timing.

Fixes #2065

Related: maplibre/maplibre-native#4160

@JeremyBYU JeremyBYU marked this pull request as ready for review April 8, 2026 21:48
@acalcutt acalcutt force-pushed the fix/raster-dem-empty-tile-segfault branch 2 times, most recently from fc6b4c9 to 8f28ce4 Compare April 23, 2026 20:49
@acalcutt acalcutt requested a review from Copilot April 23, 2026 20:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Prevents MapLibre GL Native crashes when rendering styles that include local raster-dem (terrain) sources with missing tiles by ensuring missing DEM tiles go through MapLibre’s “no content” callback path rather than generating a 1×1 placeholder image.

Changes:

  • Treat missing raster-dem tiles as “no content” (callback()) even when sparse is explicitly false.
  • Add an inline comment documenting the segfault scenario and linking to the issue.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/serve_rendered.js
Comment on lines +1404 to +1405
// raster-dem: 1x1 placeholder causes backfillBorder segfault (#2065)
if (sparse || sourceInfo.type === 'raster-dem') {

Copilot AI Apr 23, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change prevents 1×1 placeholders for missing raster-dem tiles in the mbtiles/pmtiles path, but the http/https request path still calls createEmptyResponse() (1×1 PNG) for non-OK responses whenever options.sparse is false. If those remote requests are raster-dem tiles, the same DEM dimension mismatch/crash risk can still exist. Consider extending the raster-dem safeguard to the remote code path (or clarifying in code/docs that the fix applies only to local sources).

Copilot uses AI. Check for mistakes.
Comment thread src/serve_rendered.js
Comment on lines +1404 to 1407
// raster-dem: 1x1 placeholder causes backfillBorder segfault (#2065)
if (sparse || sourceInfo.type === 'raster-dem') {
callback();
return;

Copilot AI Apr 23, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There’s no regression test covering the crash scenario this guard is meant to prevent (raster-dem source with sparse explicitly false and at least one missing tile next to a real neighbor). Adding an integration test that renders a tile/static image using a raster-dem MBTiles/PMTiles source with a deliberately missing tile would help ensure this doesn’t regress and that the server doesn’t crash under that configuration.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

You could download gebco terrarium raster-dem tiles from my server at https://tiles.wifidb.net/data/ocean-rgb/{z}/{x}/{y}.webp (https://tiles.wifidb.net/data/ocean-rgb.json) for the test, what has z0 - Z8 tiles. the gebco terrain tiles are public domain licensed, so they are good for tests

Missing raster-dem tiles were routed through createEmptyResponse() when
sparse=false, generating a 1x1 PNG. This caused a dimension mismatch
segfault in maplibre-native's DEMData::backfillBorder().

Fixes maptiler#2065

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@acalcutt acalcutt force-pushed the fix/raster-dem-empty-tile-segfault branch from 8f28ce4 to d852b87 Compare May 21, 2026 14:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Missing raster-dem tiles should always use callback(), not createEmptyResponse() — can cause segfault in maplibre-native

3 participants