Skip to content

Commit d10e27d

Browse files
committed
Improved error handling of broken databases.
Previously we Java was throwing IllegalArgumentExceptions when we tried to use an invalid pointer. Also added size checks for booleans, floats, and doubles.
1 parent 7e4322d commit d10e27d

4 files changed

Lines changed: 95 additions & 7 deletions

File tree

src/main/java/com/maxmind/maxminddb/Decoder.java

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ void setOffset(int offset) {
8181

8282
Result decode(int offset) throws IOException {
8383
ByteBuffer buffer = this.threadBuffer.get();
84+
if (offset >= buffer.capacity()) {
85+
throw new InvalidDatabaseException(
86+
"The MaxMind DB file's data section contains bad data: "
87+
+ "pointer larger than the database.");
88+
}
8489

8590
buffer.position(offset);
8691
int ctrlByte = 0xFF & buffer.get();
@@ -142,9 +147,9 @@ private Result decodeByType(Type type, int offset, int size)
142147
TextNode s = new TextNode(this.decodeString(size));
143148
return new Result(s, newOffset);
144149
case DOUBLE:
145-
return new Result(this.decodeDouble(), newOffset);
150+
return new Result(this.decodeDouble(size), newOffset);
146151
case FLOAT:
147-
return new Result(this.decodeFloat(), newOffset);
152+
return new Result(this.decodeFloat(size), newOffset);
148153
case BYTES:
149154
BinaryNode b = new BinaryNode(this.getByteArray(size));
150155
return new Result(b, newOffset);
@@ -231,16 +236,36 @@ private BigIntegerNode decodeBigInteger(int size) {
231236
return new BigIntegerNode(new BigInteger(1, bytes));
232237
}
233238

234-
private DoubleNode decodeDouble() {
239+
private DoubleNode decodeDouble(int size) throws InvalidDatabaseException {
240+
if (size != 8) {
241+
throw new InvalidDatabaseException(
242+
"The MaxMind DB file's data section contains bad data: "
243+
+ "invalid size of double.");
244+
}
235245
return new DoubleNode(this.threadBuffer.get().getDouble());
236246
}
237247

238-
private FloatNode decodeFloat() {
248+
private FloatNode decodeFloat(int size) throws InvalidDatabaseException {
249+
if (size != 4) {
250+
throw new InvalidDatabaseException(
251+
"The MaxMind DB file's data section contains bad data: "
252+
+ "invalid size of float.");
253+
}
239254
return new FloatNode(this.threadBuffer.get().getFloat());
240255
}
241256

242-
private static BooleanNode decodeBoolean(int size) {
243-
return size == 0 ? BooleanNode.FALSE : BooleanNode.TRUE;
257+
private static BooleanNode decodeBoolean(int size)
258+
throws InvalidDatabaseException {
259+
switch (size) {
260+
case 0:
261+
return BooleanNode.FALSE;
262+
case 1:
263+
return BooleanNode.TRUE;
264+
default:
265+
throw new InvalidDatabaseException(
266+
"The MaxMind DB file's data section contains bad data: "
267+
+ "invalid size of boolean.");
268+
}
244269
}
245270

246271
private Result decodeArray(int size, int offset) throws IOException {

src/main/java/com/maxmind/maxminddb/MaxMindDbReader.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,12 @@ private JsonNode resolveDataPointer(int pointer) throws IOException {
176176
int resolved = (pointer - this.metadata.nodeCount)
177177
+ this.metadata.searchTreeSize;
178178

179+
if (resolved >= this.threadBuffer.get().capacity()) {
180+
throw new InvalidDatabaseException(
181+
"The MaxMind DB file's search tree is corrupt: "
182+
+ "contains pointer larger than the database.");
183+
}
184+
179185
// We only want the data from the decoder, not the offset where it was
180186
// found.
181187
return this.decoder.decode(resolved).getNode();

src/test/java/com/maxmind/maxminddb/ReaderTest.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.maxmind.maxminddb;
22

3+
import static org.hamcrest.CoreMatchers.containsString;
34
import static org.junit.Assert.assertArrayEquals;
45
import static org.junit.Assert.assertEquals;
56
import static org.junit.Assert.assertNull;
@@ -11,10 +12,13 @@
1112
import java.net.InetAddress;
1213
import java.net.URI;
1314
import java.net.URISyntaxException;
15+
import java.net.UnknownHostException;
1416
import java.util.HashMap;
1517
import java.util.Map;
1618

19+
import org.junit.Rule;
1720
import org.junit.Test;
21+
import org.junit.rules.ExpectedException;
1822

1923
import com.fasterxml.jackson.databind.JsonNode;
2024
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -138,6 +142,59 @@ public void testZeros() throws URISyntaxException, IOException {
138142
assertEquals(BigInteger.ZERO, record.get("uint128").bigIntegerValue());
139143
}
140144

145+
@Rule
146+
public ExpectedException thrown = ExpectedException.none();
147+
148+
@Test
149+
public void testBrokenDatabase() throws URISyntaxException, IOException {
150+
URI file = ReaderTest.class
151+
.getResource(
152+
"/maxmind-db/test-data/GeoIP2-City-Test-Broken-Double-Format.mmdb")
153+
.toURI();
154+
155+
MaxMindDbReader reader = new MaxMindDbReader(new File(file));
156+
157+
this.thrown.expect(InvalidDatabaseException.class);
158+
this.thrown
159+
.expectMessage(containsString("The MaxMind DB file's data section contains bad data"));
160+
161+
reader.get(InetAddress.getByName("2001:220::"));
162+
}
163+
164+
@Test
165+
public void testBrokenSearchTreePointer() throws URISyntaxException,
166+
IOException {
167+
URI file = ReaderTest.class
168+
.getResource(
169+
"/maxmind-db/test-data/MaxMind-DB-test-broken-pointers-24.mmdb")
170+
.toURI();
171+
172+
MaxMindDbReader reader = new MaxMindDbReader(new File(file));
173+
174+
this.thrown.expect(InvalidDatabaseException.class);
175+
this.thrown
176+
.expectMessage(containsString("The MaxMind DB file's search tree is corrupt"));
177+
178+
reader.get(InetAddress.getByName("1.1.1.32"));
179+
}
180+
181+
@Test
182+
public void testBrokenDataPointer() throws UnknownHostException,
183+
IOException, URISyntaxException {
184+
URI file = ReaderTest.class
185+
.getResource(
186+
"/maxmind-db/test-data/MaxMind-DB-test-broken-pointers-24.mmdb")
187+
.toURI();
188+
189+
MaxMindDbReader reader = new MaxMindDbReader(new File(file));
190+
191+
this.thrown.expect(InvalidDatabaseException.class);
192+
this.thrown
193+
.expectMessage(containsString("The MaxMind DB file's data section contains bad data"));
194+
195+
reader.get(InetAddress.getByName("1.1.1.16"));
196+
}
197+
141198
private void testMetadata(MaxMindDbReader reader, int ipVersion,
142199
long recordSize) {
143200

0 commit comments

Comments
 (0)