From da05bc6369e144f7f90ea8b87667a0f8a8ff96ce Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Sun, 3 May 2026 16:24:54 +1000 Subject: [PATCH 1/2] MDEV-35595 UBSAN: runtime error: load of value 3 for wkbByteOrder When creating Geometry objects from raw bytes, validate the byte order is not > wkb_ndr, a constant for little endian being the upper bound of the wkbByteOrder. The 3 validation locations are the callers to the other init_from_wkb functions for other geometry types, which prevent an invalid value from reaching wkb_get_uint. As wkb[0], a char can signed, use uchar for comparision. The other value in the wkb_ndr enum is wkb_xdr being 0 which makes > wk_ndr a valid test of the two allowed values. --- mysql-test/main/gis.result | 10 ++++++++++ mysql-test/main/gis.test | 9 +++++++++ sql/spatial.cc | 12 +++++++----- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/mysql-test/main/gis.result b/mysql-test/main/gis.result index 90c8f34e68d4f..169343b568e68 100644 --- a/mysql-test/main/gis.result +++ b/mysql-test/main/gis.result @@ -5602,3 +5602,13 @@ SELECT HEX(ST_GeometryN(0x000000000107000000010000000101000000, 1)); HEX(ST_GeometryN(0x000000000107000000010000000101000000, 1)) NULL # End of 10.6 tests +# +# MDEV-35595 UBSAN: runtime error: load of value 3 for 'wkbByteOrder' +# +SELECT ST_GEOMFROMWKB (0x01060000000100000003020000000100000000000000000000000000000000000000) as g; +g +NULL +SELECT ST_GEOMFROMWKB (0x01060000000100000083020000000100000000000000000000000000000000000000) as g; +g +NULL +# End of 10.11 tests diff --git a/mysql-test/main/gis.test b/mysql-test/main/gis.test index 24d5582ee3c8d..641df9f11c1e6 100644 --- a/mysql-test/main/gis.test +++ b/mysql-test/main/gis.test @@ -3574,3 +3574,12 @@ SELECT ST_GeomFromWKB(x'01070000000100000001070000000100000001070000000100000001 SELECT HEX(ST_GeometryN(0x000000000107000000010000000101000000, 1)); --echo # End of 10.6 tests + +--echo # +--echo # MDEV-35595 UBSAN: runtime error: load of value 3 for 'wkbByteOrder' +--echo # + +SELECT ST_GEOMFROMWKB (0x01060000000100000003020000000100000000000000000000000000000000000000) as g; +SELECT ST_GEOMFROMWKB (0x01060000000100000083020000000100000000000000000000000000000000000000) as g; + +--echo # End of 10.11 tests diff --git a/sql/spatial.cc b/sql/spatial.cc index 1d172b44dd108..3d331f9785bd0 100644 --- a/sql/spatial.cc +++ b/sql/spatial.cc @@ -472,7 +472,7 @@ Geometry *Geometry::create_from_wkb(Geometry_buffer *buffer, uint32 geom_type; Geometry *geom; - if (len < WKB_HEADER_SIZE) + if (len < WKB_HEADER_SIZE || (uchar) wkb[0] > wkb_ndr) return NULL; wkbByteOrder bo= (wkbByteOrder)wkb[0]; geom_type= wkb_get_uint(wkb+1, bo); @@ -2907,8 +2907,9 @@ uint Gis_multi_polygon::init_from_wkb(const char *wkb, uint len, Gis_polygon p; int p_len; - if (len < WKB_HEADER_SIZE || - res->reserve(WKB_HEADER_SIZE, 512)) + if (len < WKB_HEADER_SIZE + || (uchar) wkb[0] > wkb_ndr + || res->reserve(WKB_HEADER_SIZE, 512)) return 0; res->q_append((char) wkb_ndr); res->q_append((uint32) wkb_polygon); @@ -3424,8 +3425,9 @@ uint Gis_geometry_collection::init_from_wkb(const char *wkb, uint len, int g_len; uint32 wkb_type; - if (len < WKB_HEADER_SIZE || - res->reserve(WKB_HEADER_SIZE, 512)) + if (len < WKB_HEADER_SIZE + || (uchar) wkb[0] > wkb_ndr + || res->reserve(WKB_HEADER_SIZE, 512)) return 0; wkbByteOrder bo= (wkbByteOrder)wkb[0]; From 846745f5f5d9663cc46a0a8afd57eab93c40bae2 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Mon, 1 Jun 2026 17:35:42 +1000 Subject: [PATCH 2/2] MDEV-26813 UBSAN: bool wkbByteOrder via Geometry::wkbByteOrder (multi_line_string) Parsing a wkb of a multi_line_string needs to validate the wkbByteOrder field for <= wkb_ndr to be valid, otherwise return null. Note that wkb[0] is a char, so can be signed, hence the cast and > 0x80 values in the test case. --- mysql-test/main/gis.result | 15 +++++++++++++++ mysql-test/main/gis.test | 9 +++++++++ sql/spatial.cc | 3 +++ 3 files changed, 27 insertions(+) diff --git a/mysql-test/main/gis.result b/mysql-test/main/gis.result index 169343b568e68..5dcedad43d28d 100644 --- a/mysql-test/main/gis.result +++ b/mysql-test/main/gis.result @@ -5611,4 +5611,19 @@ NULL SELECT ST_GEOMFROMWKB (0x01060000000100000083020000000100000000000000000000000000000000000000) as g; g NULL +# +# MDEV-26813 UBSAN: load of value 2 for bool 'wkbByteOrder' via 'Geometry::wkbByteOrder' for multi_line_string +# +SELECT ST_GEOMFROMWKB (0x01070000000100000002010000000000000000000000) as g; +g +NULL +SELECT ST_GEOMFROMWKB (0x01070000000100000082010000000000000000000000) as g; +g +NULL +SELECT ST_GEOMFROMWKB (0x01050000000100000002040000000100000000000000000000000000000000000000) as g; +g +NULL +SELECT ST_GEOMFROMWKB (0x01050000000100000082040000000100000000000000000000000000000000000000) as g; +g +NULL # End of 10.11 tests diff --git a/mysql-test/main/gis.test b/mysql-test/main/gis.test index 641df9f11c1e6..745c60501f90f 100644 --- a/mysql-test/main/gis.test +++ b/mysql-test/main/gis.test @@ -3582,4 +3582,13 @@ SELECT HEX(ST_GeometryN(0x000000000107000000010000000101000000, 1)); SELECT ST_GEOMFROMWKB (0x01060000000100000003020000000100000000000000000000000000000000000000) as g; SELECT ST_GEOMFROMWKB (0x01060000000100000083020000000100000000000000000000000000000000000000) as g; +--echo # +--echo # MDEV-26813 UBSAN: load of value 2 for bool 'wkbByteOrder' via 'Geometry::wkbByteOrder' for multi_line_string +--echo # + +SELECT ST_GEOMFROMWKB (0x01070000000100000002010000000000000000000000) as g; +SELECT ST_GEOMFROMWKB (0x01070000000100000082010000000000000000000000) as g; +SELECT ST_GEOMFROMWKB (0x01050000000100000002040000000100000000000000000000000000000000000000) as g; +SELECT ST_GEOMFROMWKB (0x01050000000100000082040000000100000000000000000000000000000000000000) as g; + --echo # End of 10.11 tests diff --git a/sql/spatial.cc b/sql/spatial.cc index 3d331f9785bd0..6177c2d9727b8 100644 --- a/sql/spatial.cc +++ b/sql/spatial.cc @@ -2533,6 +2533,9 @@ uint Gis_multi_line_string::init_from_wkb(const char *wkb, uint len, Gis_line_string ls; int ls_len; + if ((uchar) wkb[0] > wkb_ndr) /* invalid */ + return 0; + if ((len < WKB_HEADER_SIZE) || res->reserve(WKB_HEADER_SIZE, 512)) return 0;