diff --git a/modules/core/src/main/java/org/locationtech/jts/io/WKBReader.java b/modules/core/src/main/java/org/locationtech/jts/io/WKBReader.java index c4bb54647d..865966ee01 100644 --- a/modules/core/src/main/java/org/locationtech/jts/io/WKBReader.java +++ b/modules/core/src/main/java/org/locationtech/jts/io/WKBReader.java @@ -78,6 +78,8 @@ */ public class WKBReader { + private static final int MAXIMUM_NESTING_DEPTH = 1000; + /** * Converts a hexadecimal string to a byte array. * The hexadecimal digit symbols are case-insensitive. @@ -189,7 +191,7 @@ private Geometry read(InStream is, int maxCoordNum) */ this.maxNumFieldValue = maxCoordNum; dis.setInStream(is); - return readGeometry(0); + return readGeometry(0, 0); } private int readNumField(String fieldName) throws IOException, ParseException { @@ -201,7 +203,7 @@ private int readNumField(String fieldName) throws IOException, ParseException { return num; } - private Geometry readGeometry(int SRID) + private Geometry readGeometry(int SRID, int nestingDepth) throws IOException, ParseException { @@ -276,16 +278,16 @@ else if(isStrict) geom = readPolygon(ordinateFlags); break; case WKBConstants.wkbMultiPoint : - geom = readMultiPoint(SRID); + geom = readMultiPoint(SRID, nestingDepth); break; case WKBConstants.wkbMultiLineString : - geom = readMultiLineString(SRID); + geom = readMultiLineString(SRID, nestingDepth); break; case WKBConstants.wkbMultiPolygon : - geom = readMultiPolygon(SRID); + geom = readMultiPolygon(SRID, nestingDepth); break; case WKBConstants.wkbGeometryCollection : - geom = readGeometryCollection(SRID); + geom = readGeometryCollection(SRID, nestingDepth); break; default: throw new ParseException("Unknown WKB type " + geometryType); @@ -349,12 +351,12 @@ private Polygon readPolygon(EnumSet ordinateFlags) throws IOException, return factory.createPolygon(shell, holes); } - private MultiPoint readMultiPoint(int SRID) throws IOException, ParseException + private MultiPoint readMultiPoint(int SRID, int nestingDepth) throws IOException, ParseException { int numGeom = readNumField(FIELD_NUMELEMS); Point[] geoms = new Point[numGeom]; for (int i = 0; i < numGeom; i++) { - Geometry g = readGeometry(SRID); + Geometry g = readGeometry(SRID, nestingDepth + 1); if (! (g instanceof Point)) throw new ParseException(INVALID_GEOM_TYPE_MSG + "MultiPoint"); geoms[i] = (Point) g; @@ -362,12 +364,12 @@ private MultiPoint readMultiPoint(int SRID) throws IOException, ParseException return factory.createMultiPoint(geoms); } - private MultiLineString readMultiLineString(int SRID) throws IOException, ParseException + private MultiLineString readMultiLineString(int SRID, int nestingDepth) throws IOException, ParseException { int numGeom = readNumField(FIELD_NUMELEMS); LineString[] geoms = new LineString[numGeom]; for (int i = 0; i < numGeom; i++) { - Geometry g = readGeometry(SRID); + Geometry g = readGeometry(SRID, nestingDepth + 1); if (! (g instanceof LineString)) throw new ParseException(INVALID_GEOM_TYPE_MSG + "MultiLineString"); geoms[i] = (LineString) g; @@ -375,13 +377,13 @@ private MultiLineString readMultiLineString(int SRID) throws IOException, ParseE return factory.createMultiLineString(geoms); } - private MultiPolygon readMultiPolygon(int SRID) throws IOException, ParseException + private MultiPolygon readMultiPolygon(int SRID, int nestingDepth) throws IOException, ParseException { int numGeom = readNumField(FIELD_NUMELEMS); Polygon[] geoms = new Polygon[numGeom]; for (int i = 0; i < numGeom; i++) { - Geometry g = readGeometry(SRID); + Geometry g = readGeometry(SRID, nestingDepth + 1); if (! (g instanceof Polygon)) throw new ParseException(INVALID_GEOM_TYPE_MSG + "MultiPolygon"); geoms[i] = (Polygon) g; @@ -389,12 +391,17 @@ private MultiPolygon readMultiPolygon(int SRID) throws IOException, ParseExcepti return factory.createMultiPolygon(geoms); } - private GeometryCollection readGeometryCollection(int SRID) throws IOException, ParseException + private GeometryCollection readGeometryCollection(int SRID, int nestingDepth) + throws IOException, ParseException { + if (nestingDepth >= MAXIMUM_NESTING_DEPTH) { + throw new ParseException("GeometryCollection nesting depth exceeds maximum of " + + MAXIMUM_NESTING_DEPTH); + } int numGeom = readNumField(FIELD_NUMELEMS); Geometry[] geoms = new Geometry[numGeom]; for (int i = 0; i < numGeom; i++) { - geoms[i] = readGeometry(SRID); + geoms[i] = readGeometry(SRID, nestingDepth + 1); } return factory.createGeometryCollection(geoms); } diff --git a/modules/core/src/main/java/org/locationtech/jts/io/WKTReader.java b/modules/core/src/main/java/org/locationtech/jts/io/WKTReader.java index 99ae9a95a8..29070d154d 100644 --- a/modules/core/src/main/java/org/locationtech/jts/io/WKTReader.java +++ b/modules/core/src/main/java/org/locationtech/jts/io/WKTReader.java @@ -171,6 +171,8 @@ */ public class WKTReader { + private static final int MAXIMUM_NESTING_DEPTH = 1000; + private static final String COMMA = ","; private static final String L_PAREN = "("; private static final String R_PAREN = ")"; @@ -284,7 +286,7 @@ public Geometry read(String wellKnownText) throws ParseException { public Geometry read(Reader reader) throws ParseException { StreamTokenizer tokenizer = createTokenizer(reader); try { - return readGeometryTaggedText(tokenizer); + return readGeometryTaggedText(tokenizer, 0); } catch (IOException e) { throw new ParseException(e.toString()); @@ -738,7 +740,7 @@ private static String tokenString(StreamTokenizer tokenizer) *@throws IOException if an I/O error occurs * @param tokenizer tokenizer over a stream of text in Well-known Text */ - private Geometry readGeometryTaggedText(StreamTokenizer tokenizer) throws IOException, ParseException { + private Geometry readGeometryTaggedText(StreamTokenizer tokenizer, int nestingDepth) throws IOException, ParseException { String type; EnumSet ordinateFlags = EnumSet.of(Ordinate.X, Ordinate.Y); @@ -751,10 +753,11 @@ private Geometry readGeometryTaggedText(StreamTokenizer tokenizer) throws IOExce } else if (type.endsWith(WKTConstants.M)) { ordinateFlags.add(Ordinate.M); } - return readGeometryTaggedText(tokenizer, type, ordinateFlags); + return readGeometryTaggedText(tokenizer, type, ordinateFlags, nestingDepth); } - private Geometry readGeometryTaggedText(StreamTokenizer tokenizer, String type, EnumSet ordinateFlags) + private Geometry readGeometryTaggedText(StreamTokenizer tokenizer, String type, EnumSet ordinateFlags, + int nestingDepth) throws IOException, ParseException { if (ordinateFlags.size() == 2) { @@ -796,7 +799,7 @@ else if (isTypeName(tokenizer, type, WKTConstants.MULTIPOLYGON)) { return readMultiPolygonText(tokenizer, ordinateFlags); } else if (isTypeName(tokenizer, type, WKTConstants.GEOMETRYCOLLECTION)) { - return readGeometryCollectionText(tokenizer, ordinateFlags); + return readGeometryCollectionText(tokenizer, ordinateFlags, nestingDepth); } throw parseErrorWithLine(tokenizer, "Unknown geometry type: " + type); } @@ -1002,14 +1005,19 @@ private MultiPolygon readMultiPolygonText(StreamTokenizer tokenizer, EnumSet ordinateFlags) throws IOException, ParseException { + private GeometryCollection readGeometryCollectionText(StreamTokenizer tokenizer, EnumSet ordinateFlags, + int nestingDepth) throws IOException, ParseException { + if (nestingDepth >= MAXIMUM_NESTING_DEPTH) { + throw new ParseException("GeometryCollection nesting depth exceeds maximum of " + + MAXIMUM_NESTING_DEPTH); + } String nextToken = getNextEmptyOrOpener(tokenizer); if (nextToken.equals(WKTConstants.EMPTY)) { return geometryFactory.createGeometryCollection(); } List geometries = new ArrayList(); do { - Geometry geometry = readGeometryTaggedText(tokenizer); + Geometry geometry = readGeometryTaggedText(tokenizer, nestingDepth + 1); geometries.add(geometry); nextToken = getNextCloserOrComma(tokenizer); } while (nextToken.equals(COMMA)); @@ -1019,4 +1027,3 @@ private GeometryCollection readGeometryCollectionText(StreamTokenizer tokenizer, } } - diff --git a/modules/core/src/test/java/org/locationtech/jts/io/WKBReaderTest.java b/modules/core/src/test/java/org/locationtech/jts/io/WKBReaderTest.java index f191a80d60..f3a3f279f7 100644 --- a/modules/core/src/test/java/org/locationtech/jts/io/WKBReaderTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/io/WKBReaderTest.java @@ -30,6 +30,8 @@ */ public class WKBReaderTest extends TestCase { + private static final int MAXIMUM_NESTING_DEPTH = 1000; + public static void main(String args[]) { TestRunner.run(WKBReaderTest.class); } @@ -211,6 +213,15 @@ public void testInvalidWkbShouldBeReadable() throws ParseException { assertEquals(2029, lastSubGeometry.getSRID()); } + public void testNestedGeometryCollection() throws ParseException + { + String wkb = "01010000000000000000000000000000000000000000"; + for (int i = 0; i <= MAXIMUM_NESTING_DEPTH; i++) { + wkb = "010700000001000000" + wkb; + } + checkWKBParseException(wkb); + } + /** * Tests WKB that requests a huge number of points. * Not yet implemented satisfactorily. diff --git a/modules/core/src/test/java/org/locationtech/jts/io/WKTReaderParseErrorTest.java b/modules/core/src/test/java/org/locationtech/jts/io/WKTReaderParseErrorTest.java index f11ac1d2a6..bc49595f46 100644 --- a/modules/core/src/test/java/org/locationtech/jts/io/WKTReaderParseErrorTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/io/WKTReaderParseErrorTest.java @@ -24,6 +24,8 @@ public class WKTReaderParseErrorTest extends TestCase { + private static final int MAXIMUM_NESTING_DEPTH = 1000; + public static void main(String args[]) { TestRunner.run(WKTReaderParseErrorTest.class); } @@ -155,6 +157,15 @@ public void testMissingComponents() throws IOException readWithParseException("GEOMETRYCOLLECTION ()"); readWithParseException("GEOMETRYCOLLECTION"); } + + public void testNestedGeometryCollection() throws IOException + { + String wkt = "POINT (0 0)"; + for (int i = 0; i <= MAXIMUM_NESTING_DEPTH; i++) { + wkt = "GEOMETRYCOLLECTION (" + wkt + ")"; + } + readWithParseException(wkt); + } public void testEmptyComponents() throws ParseException, IOException { readWithInvalidException("POLYGON( EMPTY, (1 1,2 2,1 2,1 1))"); @@ -190,4 +201,3 @@ private void readWithInvalidException(String wkt) fail(); } } -