You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
iOS, MapLibre Native ios-v6.27.0 (via @maplibre/maplibre-react-native 11.3.4), observed in production on iOS 26.5.2
The affected code is shared (src/mbgl/util/mapbox.cpp), so Android should reproduce as well
Steps to reproduce
Use any style whose glyphs points at the default MapLibre tile server host, e.g. https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf
(tiles can come from anywhere — only the glyph URL matters; sources in our style are on a different host entirely)
Create an offline pack from that style (OfflineDownload / MLNOfflineStorage addPack…)
As soon as the style resource response is processed, the process aborts with SIGABRT
100% reproducible.
Root cause
OfflineDownload::activateDownload()'s style response callback calls mbgl::util::mapbox::canonicalizeGlyphURL(tileServerOptions, url). Because the glyph URL matches
the default TileServerOptions::MapLibreConfiguration()baseURL (https://demotiles.maplibre.org), isNormalizedSourceURL(...) returns true and createTokenMap(tileServerOptions.glyphsTemplate(), url) runs.
createTokenMap (src/mbgl/util/mapbox.cpp) builds a std::regex from the glyphs template after
substituting only the {domain|path|directory|filename|extension} tokens:
The default MapLibre glyphs template is /font/{fontstack}/{start}-{end}.pbf, so {fontstack}, {start} and {end} survive into templatePattern. {f etc. are invalid ECMAScript brace
quantifiers, so the std::regex constructor throws regex_error. Nothing on the DatabaseFileSource thread catches it → std::terminate → SIGABRT.
(isNormalizedSourceURL happens to survive because its token pattern \{.+\} greedily swallows {fontstack}/{start}-{end} in one replacement — which is why execution reaches createTokenMap
at all.)
In createTokenMap (and isNormalizedSourceURL), regex-escape the non-token parts of the
template — or at least strip/escape leftover {...} tokens — before constructing std::regex,
so arbitrary glyphsTemplate values can't produce an invalid pattern.
Wrap the canonicalization calls in OfflineDownload in try/catch and fall back to the raw
URL, so a canonicalization failure degrades gracefully instead of terminating the host app.
Related
Offline download crashes #177 reported offline downloads crashing with demotiles in 2022 and already suspected the regex
validation in mapbox.cpp; it was closed as "demotiles doesn't support offline" after the
maintainer hit a different symptom (304 handling). The uncaught-throw path reported here is
still present in ios-v6.27.0, and it triggers even when only the glyphs live on
demotiles.maplibre.org (a common configuration, since demotiles is MapLibre's public font
endpoint) while all tile sources are elsewhere.
Host your glyphs anywhere that is notdemotiles.maplibre.org (we moved to OpenFreeMap's
font endpoint); the canonicalizer then leaves the URL untouched and the download completes.
Platform / version
ios-v6.27.0(via@maplibre/maplibre-react-native11.3.4), observed in production on iOS 26.5.2src/mbgl/util/mapbox.cpp), so Android should reproduce as wellSteps to reproduce
glyphspoints at the default MapLibre tile server host, e.g.https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf(tiles can come from anywhere — only the glyph URL matters; sources in our style are on a different host entirely)
OfflineDownload/MLNOfflineStorage addPack…)100% reproducible.
Root cause
OfflineDownload::activateDownload()'s style response callback callsmbgl::util::mapbox::canonicalizeGlyphURL(tileServerOptions, url). Because the glyph URL matchesthe default
TileServerOptions::MapLibreConfiguration()baseURL(https://demotiles.maplibre.org),isNormalizedSourceURL(...)returns true andcreateTokenMap(tileServerOptions.glyphsTemplate(), url)runs.createTokenMap(src/mbgl/util/mapbox.cpp) builds astd::regexfrom the glyphs template aftersubstituting only the
{domain|path|directory|filename|extension}tokens:The default MapLibre glyphs template is
/font/{fontstack}/{start}-{end}.pbf, so{fontstack},{start}and{end}survive intotemplatePattern.{fetc. are invalid ECMAScript bracequantifiers, so the
std::regexconstructor throwsregex_error. Nothing on theDatabaseFileSourcethread catches it →std::terminate→ SIGABRT.(
isNormalizedSourceURLhappens to survive because its token pattern\{.+\}greedily swallows{fontstack}/{start}-{end}in one replacement — which is why execution reachescreateTokenMapat all.)
Symbolicated crash (thread
org.maplibre.mbgl.DatabaseFileSource)Suggested fix
Any (ideally both) of:
createTokenMap(andisNormalizedSourceURL), regex-escape the non-token parts of thetemplate — or at least strip/escape leftover
{...}tokens — before constructingstd::regex,so arbitrary
glyphsTemplatevalues can't produce an invalid pattern.OfflineDownloadintry/catchand fall back to the rawURL, so a canonicalization failure degrades gracefully instead of terminating the host app.
Related
validation in
mapbox.cpp; it was closed as "demotiles doesn't support offline" after themaintainer hit a different symptom (304 handling). The uncaught-throw path reported here is
still present in
ios-v6.27.0, and it triggers even when only the glyphs live ondemotiles.maplibre.org (a common configuration, since demotiles is MapLibre's public font
endpoint) while all tile sources are elsewhere.
path terminating the host app).
Workaround for anyone hitting this
Host your glyphs anywhere that is not
demotiles.maplibre.org(we moved to OpenFreeMap'sfont endpoint); the canonicalizer then leaves the URL untouched and the download completes.