diff --git a/CHANGELOG.md b/CHANGELOG.md index b0c8de97..6c4e2aa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ CHANGELOG * IMPORTANT: Java 8 is now required. If you need Java 7 support, please continue using 1.3.1 or earlier. +* The decoder will now throw an `InvalidDatabaseException` on an invalid + control byte in the data section rather than an + `ArrayIndexOutOfBoundsException`. Reported by Edwin Delgado H. GitHub + #68. 1.3.1 (2020-03-03) ------------------ diff --git a/src/main/java/com/maxmind/db/Decoder.java b/src/main/java/com/maxmind/db/Decoder.java index d9968935..8be5b91c 100644 --- a/src/main/java/com/maxmind/db/Decoder.java +++ b/src/main/java/com/maxmind/db/Decoder.java @@ -49,16 +49,19 @@ enum Type { // the speed by about 5000 requests per second on my machine. final static Type[] values = Type.values(); - static Type get(int i) { + static Type get(int i) throws InvalidDatabaseException { + if (i >= Type.values.length) { + throw new InvalidDatabaseException("The MaxMind DB file's data section contains bad data"); + } return Type.values[i]; } - private static Type get(byte b) { + private static Type get(byte b) throws InvalidDatabaseException { // bytes are signed, but we want to treat them as unsigned here return Type.get(b & 0xFF); } - static Type fromControlByte(int b) { + static Type fromControlByte(int b) throws InvalidDatabaseException { // The type is encoded in the first 3 bits of the byte. return Type.get((byte) ((0xFF & b) >>> 5)); } diff --git a/src/test/java/com/maxmind/db/DecoderTest.java b/src/test/java/com/maxmind/db/DecoderTest.java index c9b4d8b1..f6b0c74d 100644 --- a/src/test/java/com/maxmind/db/DecoderTest.java +++ b/src/test/java/com/maxmind/db/DecoderTest.java @@ -1,8 +1,5 @@ package com.maxmind.db; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; - import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; @@ -23,6 +20,10 @@ import com.fasterxml.jackson.databind.node.FloatNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.*; + @SuppressWarnings({"boxing", "static-method"}) public class DecoderTest { @@ -415,6 +416,19 @@ public void testArrays() throws IOException { DecoderTest.testTypeDecoding(Decoder.Type.ARRAY, DecoderTest.arrays()); } + @Test + public void testInvalidControlByte() throws IOException { + try (FileChannel fc = DecoderTest.getFileChannel(new byte[]{0x0, 0xF})) { + MappedByteBuffer mmap = fc.map(MapMode.READ_ONLY, 0, fc.size()); + + Decoder decoder = new Decoder(new CHMCache(), mmap, 0); + InvalidDatabaseException ex = assertThrows( + InvalidDatabaseException.class, + () -> decoder.decode(0)); + assertThat(ex.getMessage(), containsString("The MaxMind DB file's data section contains bad data")); + } + } + private static void testTypeDecoding(Decoder.Type type, Map tests) throws IOException {