Skip to content

flatgeobuf: bound geometry ring ends to the xy coordinate count - #7645

Open
nvxbug wants to merge 3 commits into
MapServer:mainfrom
nvxbug:fgb-geometry-bounds
Open

nvxbug wants to merge 3 commits into
MapServer:mainfrom
nvxbug:fgb-geometry-bounds

Conversation

@nvxbug

@nvxbug nvxbug commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do?

GeometryReader in src/flatgeobuf/geometryreader.cpp builds a shapeObj from an .fgb feature's coordinates. For a polygon the per-ring end indices come from the geometry's ends() array in the file, and readLineObj() used them to walk m_xy (and the parallel z/m arrays) without checking them against the number of coordinate pairs actually present in xy(). A ring end index past the coordinate count reads past the buffer; readPoint() had the same unchecked indexing. The flatbuffer is not verified, so those sizes are file-controlled.

The reader now records the available coordinate-pair count when xy is read and rejects any ring range that does not fit it (and any z/m array shorter than the requested points) before allocating or copying. A rejected ring is dropped rather than kept as an empty one, because shapeObj consumers do not expect empty parts: msIsOuterRing() indexes line[r].point[0..2] without looking at numpoints. Keeping the check in readLineObj/readPoint covers point, line, polygon and the multipoint/multiline/multipolygon variants that all funnel through the same code.

What are related issues/pull requests?

Same neighbourhood as #7545, which bounded the property blob; this covers the geometry-decoding side.

AI tool usage

Tasklist

  • Make sure code is correctly formatted (cf pre-commit configuration)
  • Add test case(s) in /msautotest (follow steps in Regression Testing)
  • Add documentation
  • Review
  • Adjust for comments
  • All CI builds and checks have passed

GeometryReader used per-ring end indices and point offsets taken from the .fgb file to index the xy/z/m coordinate arrays without checking them against the number of coordinates actually stored, so a ring end past the xy count read past the coordinate buffer. Record the coordinate count when xy is read and reject ranges that do not fit before allocating or copying.
@jmckenna

jmckenna commented Sep 8, 2026

Copy link
Copy Markdown
Member

cc @bjornharrtell

nvxbug added 2 commits October 8, 2026 15:04
…mpty

Zeroing numpoints on a rejected ring still left the ring in the shapeObj
with a NULL point array, and shapeObj consumers do not expect that:
msIsOuterRing() indexes line[r].point[0..2] without looking at numpoints,
so a crafted ring end turned into a NULL dereference in the WFS GML writer
instead of the out-of-bounds read. Have readLineObj() report whether it read
the range and let readPolygon()/readLineString() keep only the parts that
were read.
data/flatgeobuf-ring-invalid.fgb is a one-feature polygon whose second ring
end claims 595 coordinate pairs that are not in xy. A WFS GetFeature against
it must return the one stored ring and drop the other, which it does not do
before the fix: the read runs past the feature buffer (ASan heap-buffer-overflow
read of size 16 in readLineObj). misc-testcase runs under ASan in CI, so this
covers the same ground as the unit test did.

The unit test is dropped because it needs shapeObj's constructor and
GeometryReader::read(), neither of which is exported from the MapServer DLL,
so unit_test failed to link on the AppVeyor build.
@jratike80

Copy link
Copy Markdown

What is the effect of this bug? Does it crash the server, does it make the rendered maps bad, or what?

@nvxbug

nvxbug commented Oct 8, 2026

Copy link
Copy Markdown
Contributor Author

AppVeyor was mine: the unit test needed shapeObj's constructor and GeometryReader::read, neither of which is exported from the MapServer DLL, so unit_test wouldn't link on Windows. Moved it to /msautotest (flatgeobuf-ring-invalid.map plus a 456-byte crafted .fgb), which fits better anyway since misc-testcase runs under ASan in CI.

Running it through the real reader path also showed my first version was wrong: it zeroed numpoints on a rejected ring but left the ring in the shape with a NULL point array, and msIsOuterRing() indexes point[0..2] without looking at numpoints. So a crafted ring end became a NULL deref in the GML writer rather than the out-of-bounds read. Bad rings are dropped now. The new test aborts under ASan before the fix (heap-buffer-overflow read of size 16 in readLineObj) and passes after. irc_notify is red on an SSL handshake timeout to irc.libera.chat, unrelated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants