Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## Unreleased

- Add additional validation to help prevent OOMs during WKB parsing.

## v0.56.0

2025-11-21
Expand Down
36 changes: 34 additions & 2 deletions geom/wkb_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,11 +225,13 @@ func (p *wkbParser) parseLineString(ctype CoordinatesType) (LineString, error) {
if err != nil {
return LineString{}, err
}
floats := make([]float64, int(n)*ctype.Dimension())

if len(p.body) < 8*len(floats) {
// Ensure there are enough bytes for n points.
bytesNeeded := uint64(n) * uint64(ctype.Dimension()) * 8
if bytesNeeded > uint64(len(p.body)) {
return LineString{}, wkbSyntaxError{"unexpected EOF"}
}
floats := make([]float64, int(n)*ctype.Dimension())

var seqData []byte
if p.no {
Expand Down Expand Up @@ -274,7 +276,13 @@ func (p *wkbParser) parsePolygon(ctype CoordinatesType) (Polygon, error) {
if n == 0 {
return Polygon{}.ForceCoordinatesType(ctype), nil
}

// Each ring needs at least 4 bytes for its point count.
if uint64(n)*4 > uint64(len(p.body)) {
return Polygon{}, wkbSyntaxError{"unexpected EOF"}
}
rings := make([]LineString, n)

for i := range rings {
rings[i], err = p.parseLineString(ctype)
if err != nil {
Expand All @@ -292,7 +300,13 @@ func (p *wkbParser) parseMultiPoint(ctype CoordinatesType) (MultiPoint, error) {
if n == 0 {
return MultiPoint{}.ForceCoordinatesType(ctype), nil
}

// Each point WKB needs at least 5 bytes (1 byte order + 4 type).
if uint64(n)*5 > uint64(len(p.body)) {
return MultiPoint{}, wkbSyntaxError{"unexpected EOF"}
}
pts := make([]Point, n)

for i := uint32(0); i < n; i++ {
geom, err := p.inner()
if err != nil {
Expand All @@ -314,7 +328,13 @@ func (p *wkbParser) parseMultiLineString(ctype CoordinatesType) (MultiLineString
if n == 0 {
return MultiLineString{}.ForceCoordinatesType(ctype), nil
}

// Each linestring WKB needs at least 9 bytes (1 byte order + 4 type + 4 point count).
if uint64(n)*9 > uint64(len(p.body)) {
return MultiLineString{}, wkbSyntaxError{"unexpected EOF"}
}
lss := make([]LineString, n)

for i := uint32(0); i < n; i++ {
geom, err := p.inner()
if err != nil {
Expand All @@ -336,7 +356,13 @@ func (p *wkbParser) parseMultiPolygon(ctype CoordinatesType) (MultiPolygon, erro
if n == 0 {
return MultiPolygon{}.ForceCoordinatesType(ctype), nil
}

// Each polygon WKB needs at least 9 bytes (1 byte order + 4 type + 4 ring count).
if uint64(n)*9 > uint64(len(p.body)) {
return MultiPolygon{}, wkbSyntaxError{"unexpected EOF"}
}
polys := make([]Polygon, n)

for i := uint32(0); i < n; i++ {
geom, err := p.inner()
if err != nil {
Expand All @@ -358,7 +384,13 @@ func (p *wkbParser) parseGeometryCollection(ctype CoordinatesType) (GeometryColl
if n == 0 {
return GeometryCollection{}.ForceCoordinatesType(ctype), nil
}

// Each geometry needs at least 5 bytes (1 byte order + 4 type).
if uint64(n)*5 > uint64(len(p.body)) {
return GeometryCollection{}, wkbSyntaxError{"unexpected EOF"}
}
geoms := make([]Geometry, n)

for i := uint32(0); i < n; i++ {
geoms[i], err = p.inner()
if err != nil {
Expand Down
42 changes: 42 additions & 0 deletions geom/wkb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,48 @@ func TestWKBParserSyntaxError(t *testing.T) {
"0000000000000000", // 0
"MultiPolygon contains non-Polygon element",
},
{
"linestring with inflated point count",
"00" + // Big endian
"00000002" + // LineString XY
"0000FFFF", // n=65535 points, but no coordinate data
"unexpected EOF",
},
{
"polygon with inflated ring count",
"00" + // Big endian
"00000003" + // Polygon XY
"0000FFFF", // n=65535 rings, but no ring data
"unexpected EOF",
},
{
"multipoint with inflated point count",
"00" + // Big endian
"00000004" + // MultiPoint XY
"0000FFFF", // n=65535 points, but no point data
"unexpected EOF",
},
{
"multilinestring with inflated linestring count",
"00" + // Big endian
"00000005" + // MultiLineString XY
"0000FFFF", // n=65535 linestrings, but no data
"unexpected EOF",
},
{
"multipolygon with inflated polygon count",
"00" + // Big endian
"00000006" + // MultiPolygon XY
"0000FFFF", // n=65535 polygons, but no data
"unexpected EOF",
},
{
"geometrycollection with inflated geometry count",
"00" + // Big endian
"00000007" + // GeometryCollection XY
"0000FFFF", // n=65535 geometries, but no data
"unexpected EOF",
},
} {
t.Run(tc.description, func(t *testing.T) {
wkb := hexStringToBytes(t, tc.wkbHex)
Expand Down