diff --git a/src/java/net/jpountz/lz4/LZ4FrameInputStream.java b/src/java/net/jpountz/lz4/LZ4FrameInputStream.java index 9dce662..d1aaa74 100644 --- a/src/java/net/jpountz/lz4/LZ4FrameInputStream.java +++ b/src/java/net/jpountz/lz4/LZ4FrameInputStream.java @@ -47,6 +47,7 @@ public class LZ4FrameInputStream extends FilterInputStream { static final String NOT_SUPPORTED = "Stream unsupported"; static final String BLOCK_HASH_MISMATCH = "Block checksum mismatch"; static final String DESCRIPTOR_HASH_MISMATCH = "Stream frame descriptor corrupted"; + static final String INVALID_DESCRIPTOR = "Invalid or unsupported frame descriptor"; static final int MAGIC_SKIPPABLE_BASE = 0x184D2A50; private final LZ4SafeDecompressor decompressor; @@ -203,10 +204,16 @@ private void readHeader() throws IOException { } final byte flgByte = (byte) (flgRead & 0xFF); - final LZ4FrameOutputStream.FLG flg = LZ4FrameOutputStream.FLG.fromByte(flgByte); - headerBuffer.put(flgByte); final byte bdByte = (byte) (bdRead & 0xFF); - final LZ4FrameOutputStream.BD bd = LZ4FrameOutputStream.BD.fromByte(bdByte); + final LZ4FrameOutputStream.FLG flg; + final LZ4FrameOutputStream.BD bd; + try { + flg = LZ4FrameOutputStream.FLG.fromByte(flgByte); + bd = LZ4FrameOutputStream.BD.fromByte(bdByte); + } catch (RuntimeException e) { + throw new IOException(INVALID_DESCRIPTOR, e); + } + headerBuffer.put(flgByte); headerBuffer.put(bdByte); final StreamingXXHash32 frameStreamHash; diff --git a/src/test/net/jpountz/lz4/LZ4FrameIOStreamTest.java b/src/test/net/jpountz/lz4/LZ4FrameIOStreamTest.java index 60475ec..3de8f2b 100644 --- a/src/test/net/jpountz/lz4/LZ4FrameIOStreamTest.java +++ b/src/test/net/jpountz/lz4/LZ4FrameIOStreamTest.java @@ -16,6 +16,7 @@ * limitations under the License. */ +import net.jpountz.xxhash.XXHashFactory; import org.junit.After; import org.junit.Assert; import org.junit.Assume; @@ -866,4 +867,46 @@ public void testAvailable() throws IOException { lz4File.delete(); } } + + private static byte[] frameHeader(int flg, int bd) { + final byte[] descriptor = new byte[]{(byte) flg, (byte) bd}; + final byte hc = (byte) ((XXHashFactory.fastestInstance().hash32().hash(descriptor, 0, descriptor.length, 0) >> 8) & 0xFF); + final ByteBuffer header = ByteBuffer.allocate(7).order(ByteOrder.LITTLE_ENDIAN); + header.putInt(LZ4FrameOutputStream.MAGIC); + header.put(descriptor); + header.put(hc); + return header.array(); + } + + private static void assertInvalidDescriptor(IOException e) { + Assert.assertEquals(LZ4FrameInputStream.INVALID_DESCRIPTOR, e.getMessage()); + } + + @Test + public void testInvalidOrUnsupportedFrameDescriptor() throws IOException { + Assume.assumeTrue(testSize == 0); // does not depend on the test size + final int[][] descriptors = { + {0x40, 0x70}, // dependent blocks + {0x61, 0x70}, // dictionary ID + {0x62, 0x70}, // reserved FLG bit + {0x20, 0x70}, // unsupported version + {0x60, 0x30}, // block size indicator 3 + {0x60, 0x41}, // reserved BD bit + }; + for (final int[] d : descriptors) { + final byte[] header = frameHeader(d[0], d[1]); + try (InputStream is = new LZ4FrameInputStream(new ByteArrayInputStream(header))) { + assertInvalidDescriptor(Assert.assertThrows(IOException.class, is::read)); + } + try (InputStream is = new LZ4FrameInputStream(new ByteArrayInputStream(header))) { + assertInvalidDescriptor(Assert.assertThrows(IOException.class, () -> is.skip(1))); + } + try (LZ4FrameInputStream is = new LZ4FrameInputStream(new ByteArrayInputStream(header), true)) { + assertInvalidDescriptor(Assert.assertThrows(IOException.class, is::getExpectedContentSize)); + } + try (LZ4FrameInputStream is = new LZ4FrameInputStream(new ByteArrayInputStream(header), true)) { + assertInvalidDescriptor(Assert.assertThrows(IOException.class, is::isExpectedContentSizeDefined)); + } + } + } }