diff --git a/encoding/internal/wkbcommon/point.go b/encoding/internal/wkbcommon/point.go index 1931ddb..28ed05a 100644 --- a/encoding/internal/wkbcommon/point.go +++ b/encoding/internal/wkbcommon/point.go @@ -16,7 +16,11 @@ func unmarshalPoints(order byteOrder, data []byte) ([]orb.Point, error) { num := unmarshalUint32(order, data) data = data[4:] - if len(data) < int(num*16) { + // Compute the required byte count in 64-bit space. num is a uint32 read + // directly from the input, so num*16 can overflow a uint32 (or a 32-bit + // int) and wrap to a small value, letting an undersized buffer slip past + // this guard and panic in the read loop below. + if uint64(len(data)) < uint64(num)*16 { return nil, ErrNotWKB } diff --git a/encoding/wkb/line_string_test.go b/encoding/wkb/line_string_test.go index 5029065..c1b2f0b 100644 --- a/encoding/wkb/line_string_test.go +++ b/encoding/wkb/line_string_test.go @@ -128,3 +128,20 @@ func TestMultiLineString(t *testing.T) { }) } } + +func TestLineString_pointCountOverflow(t *testing.T) { + // A crafted little-endian linestring header claims a point count whose + // byte size (count * 16) overflows a uint32 and wraps to a small value. + // Before the length check was done in 64-bit space this slipped past the + // bounds guard and read past the end of the buffer. + data := []byte{ + 0x01, // little endian + 0x02, 0x00, 0x00, 0x00, // type: linestring + 0x01, 0x00, 0x00, 0x10, // point count 0x10000001; *16 wraps to 16 in uint32 + } + data = append(data, make([]byte, 16)...) // only one point's worth of data + + if _, err := Unmarshal(data); err != ErrNotWKB { + t.Fatalf("expected ErrNotWKB, got %v", err) + } +}