[geos-commits] [SCM] GEOS branch master updated. 95a22f44b86118a5972db2e04d0de9b4a978b167

git at osgeo.org git at osgeo.org
Tue Feb 2 11:50:33 PST 2021


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GEOS".

The branch, master has been updated
       via  95a22f44b86118a5972db2e04d0de9b4a978b167 (commit)
      from  c3c39a0d10b51bf35af4cd19ff0ea29cccbd052c (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 95a22f44b86118a5972db2e04d0de9b4a978b167
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Tue Feb 2 11:49:59 2021 -0800

    Avoid out-of-memory for malformed WKB.
    
    By simply creating a WKB input with a vertex count/ring count/geometry count that is very very large, a user can get GEOS to allocate a very large vector in advance of actually parsing the WKB to fill in that vector.
    
    Instead, we do a quick bounds check, comparing the number of objects requested and the minimum size those objects could be with the amount of space left in the input stream, and raising an exception if the minimum possible request size exceeds the amount of input data.

diff --git a/include/geos/io/WKBReader.h b/include/geos/io/WKBReader.h
index d908552..96912e5 100644
--- a/include/geos/io/WKBReader.h
+++ b/include/geos/io/WKBReader.h
@@ -158,6 +158,8 @@ private:
 
     std::unique_ptr<geom::CoordinateSequence> readCoordinateSequence(unsigned int); // throws IOException
 
+    void minMemSize(int geomType, uint64_t size);
+
     void readCoordinate(); // throws IOException
 
     // Declare type as noncopyable
diff --git a/src/io/WKBReader.cpp b/src/io/WKBReader.cpp
index f1b2952..1a57ca0 100644
--- a/src/io/WKBReader.cpp
+++ b/src/io/WKBReader.cpp
@@ -170,6 +170,45 @@ WKBReader::readHEX(std::istream& is)
     return this->read(os);
 }
 
+void
+WKBReader::minMemSize(int geomType, uint64_t size)
+{
+    uint64_t minMemSize = 0;
+    constexpr uint64_t minCoordSize = 2 * sizeof(double);
+    constexpr uint64_t minPtSize = (1+4) + minCoordSize;
+    constexpr uint64_t minLineSize = (1+4+4); // empty line
+    constexpr uint64_t minRingSize = 4; // empty ring
+    constexpr uint64_t minPolySize = (1+4+4); // empty polygon
+    constexpr uint64_t minGeomSize = minLineSize;
+
+    switch(geomType) {
+        case GEOS_LINESTRING:
+        case GEOS_LINEARRING:
+            minMemSize = size * minCoordSize;
+            break;
+        case GEOS_POLYGON:
+            minMemSize = size * minRingSize;
+            break;
+        case GEOS_MULTIPOINT:
+            minMemSize = size * minPtSize;
+            break;
+        case GEOS_MULTILINESTRING:
+            minMemSize = size * minLineSize;
+            break;
+        case GEOS_MULTIPOLYGON:
+            minMemSize = size * minPolySize;
+            break;
+        case GEOS_GEOMETRYCOLLECTION:
+            minMemSize = size * minGeomSize;
+            break;
+    }
+
+    if (dis.size() < minMemSize) {
+        throw ParseException("Input buffer is smaller than requested object size");
+    }
+}
+
+
 std::unique_ptr<Geometry>
 WKBReader::read(std::istream& is)
 {
@@ -310,6 +349,7 @@ std::unique_ptr<LineString>
 WKBReader::readLineString()
 {
     uint32_t size = dis.readUnsigned();
+    minMemSize(GEOS_LINESTRING, size);
 #if DEBUG_WKB_READER
     std::size_t << "WKB npoints: " << size << std::endl;
 #endif
@@ -321,6 +361,7 @@ std::unique_ptr<LinearRing>
 WKBReader::readLinearRing()
 {
     uint32_t size = dis.readUnsigned();
+    minMemSize(GEOS_LINEARRING, size);
 #if DEBUG_WKB_READER
     std::size_t << "WKB npoints: " << size << std::endl;
 #endif
@@ -332,6 +373,7 @@ std::unique_ptr<Polygon>
 WKBReader::readPolygon()
 {
     uint32_t numRings = dis.readUnsigned();
+    minMemSize(GEOS_POLYGON, numRings);
 
 #if DEBUG_WKB_READER
     std::size_t << "WKB numRings: " << numRings << std::endl;
@@ -361,6 +403,7 @@ std::unique_ptr<MultiPoint>
 WKBReader::readMultiPoint()
 {
     uint32_t numGeoms = dis.readUnsigned();
+    minMemSize(GEOS_MULTIPOINT, numGeoms);
     std::vector<std::unique_ptr<Geometry>> geoms(numGeoms);
 
     for(uint32_t i = 0; i < numGeoms; i++) {
@@ -379,6 +422,7 @@ std::unique_ptr<MultiLineString>
 WKBReader::readMultiLineString()
 {
     uint32_t numGeoms = dis.readUnsigned();
+    minMemSize(GEOS_MULTILINESTRING, numGeoms);
     std::vector<std::unique_ptr<Geometry>> geoms(numGeoms);
 
     for(uint32_t i = 0; i < numGeoms; i++) {
@@ -397,6 +441,7 @@ std::unique_ptr<MultiPolygon>
 WKBReader::readMultiPolygon()
 {
     uint32_t numGeoms = dis.readUnsigned();
+    minMemSize(GEOS_MULTIPOLYGON, numGeoms);
     std::vector<std::unique_ptr<Geometry>> geoms(numGeoms);
 
     for(uint32_t i = 0; i < numGeoms; i++) {
@@ -415,6 +460,7 @@ std::unique_ptr<GeometryCollection>
 WKBReader::readGeometryCollection()
 {
     uint32_t numGeoms = dis.readUnsigned();
+    minMemSize(GEOS_GEOMETRYCOLLECTION, numGeoms);
     std::vector<std::unique_ptr<Geometry>> geoms(numGeoms);
 
     for(uint32_t i = 0; i < numGeoms; i++) {
@@ -427,6 +473,7 @@ WKBReader::readGeometryCollection()
 std::unique_ptr<CoordinateSequence>
 WKBReader::readCoordinateSequence(uint32_t size)
 {
+    minMemSize(GEOS_LINESTRING, size);
     unsigned int targetDim = 2 + (hasZ ? 1 : 0);
     auto seq = factory.getCoordinateSequenceFactory()->create(size, targetDim);
     if(targetDim > inputDimension) {
diff --git a/tests/unit/io/WKBReaderTest.cpp b/tests/unit/io/WKBReaderTest.cpp
index d646284..ad8283b 100644
--- a/tests/unit/io/WKBReaderTest.cpp
+++ b/tests/unit/io/WKBReaderTest.cpp
@@ -52,6 +52,23 @@ struct test_wkbreader_data {
         wktreader(gf.get())
     {}
 
+    void
+    testParseError(const std::string& hexwkb, const std::string& errstr)
+    {
+        std::string exstr;
+        std::stringstream hexin(hexwkb);
+        try {
+            GeomPtr g(wkbreader.readHEX(hexin));
+            fail();
+        }
+        catch(const geos::util::GEOSException& ex) {
+            // std::cout << e.what() << std::endl;
+            exstr = ex.what();
+            ensure("Missing expected error", !exstr.empty());
+            ensure_equals("Parse error incorrect", exstr, errstr);
+        }
+    }
+
     GeomPtr
     readHex(const std::string& hexwkb)
     {
@@ -572,6 +589,78 @@ void object::test<24>
 
 }
 
+// Malformed WKB wrong coordinate count
+template<>
+template<>
+void object::test<25>
+()
+{
+    testParseError(
+        "010200000003000000000000000000F03F000000000000004000000000000008400000000000001040",
+        "ParseException: Input buffer is smaller than requested object size"
+    );
+}
+
+// Malformed WKB with very large coordinate count
+template<>
+template<>
+void object::test<26>
+()
+{
+    testParseError(
+        "010200000000000080000000000000F03F000000000000004000000000000008400000000000001040",
+        "ParseException: Input buffer is smaller than requested object size"
+    );
+}
+
+
+// Malformed WKB polygon with very large ring count
+template<>
+template<>
+void object::test<27>
+()
+{
+    testParseError(
+        "01030000000000008001000000000000000000F03F000000000000004000000000000008400000000000001040",
+        "ParseException: Input buffer is smaller than requested object size"
+    );
+}
+
+// Malformed WKB polygon with slightly large ring count
+template<>
+template<>
+void object::test<28>
+()
+{
+    testParseError(
+        "01030000000200000000000000",
+        "ParseException: Input buffer is smaller than requested object size"
+    );
+}
+
+// Malformed WKB polygon with more buffer than data
+template<>
+template<>
+void object::test<29>
+()
+{
+    testInput(
+        "01030000000100000000000000000000000000F03F000000000000004000000000000008400000000000001040",
+        "010300000000000000"
+    );
+}
+
+// Malformed WKB collection with overly large geom count
+template<>
+template<>
+void object::test<30>
+()
+{
+    testParseError(
+        "010700000009000000010100000000000000000010400000000000001040",
+        "ParseException: Input buffer is smaller than requested object size"
+    );
+}
 
 } // namespace tut
 

-----------------------------------------------------------------------

Summary of changes:
 include/geos/io/WKBReader.h     |  2 +
 src/io/WKBReader.cpp            | 47 ++++++++++++++++++++++
 tests/unit/io/WKBReaderTest.cpp | 89 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 138 insertions(+)


hooks/post-receive
-- 
GEOS


More information about the geos-commits mailing list