diff --git a/.gitignore b/.gitignore index 9eb92781d6..23cfe6dde7 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ target/ .recommenders/ .metadata/ +.vscode/ diff --git a/dcm4che-core/src/main/java/org/dcm4che3/io/DicomInputStream.java b/dcm4che-core/src/main/java/org/dcm4che3/io/DicomInputStream.java index b5321d5812..3298284e6e 100644 --- a/dcm4che-core/src/main/java/org/dcm4che3/io/DicomInputStream.java +++ b/dcm4che-core/src/main/java/org/dcm4che3/io/DicomInputStream.java @@ -87,6 +87,8 @@ public enum IncludeBulkData { NO, YES, URI } "Implicit VR Big Endian encoded DICOM Stream"; private static final String DEFLATED_WITH_ZLIB_HEADER = "Deflated DICOM Stream with ZLIB Header"; + /* VisibleForTesting */ static final String VALUE_TOO_LARGE = + "tag value too large, must be less than 2Gib"; private static final int ZLIB_HEADER = 0x789c; private static final int DEF_ALLOCATE_LIMIT = 0x4000000; // 64MiB @@ -412,6 +414,11 @@ public void readFully(short[] s, int off, int len) throws IOException { } } + private DicomStreamException tagValueTooLargeException() { + return new DicomStreamException( + String.format("0x%s %s", TagUtils.toHexString(tag), VALUE_TOO_LARGE)); + } + public void readHeader() throws IOException { byte[] buf = buffer; tagPos = pos; @@ -427,6 +434,7 @@ public void readHeader() throws IOException { if (explicitVR) { vr = VR.valueOf(encodedVR = ByteUtils.bytesToVR(buf, 4)); if (vr.headerLength() == 8) { + // This length can't overflow since length field is only 16 bits in this case. length = ByteUtils.bytesToUShort(buf, 6, bigEndian); return; } @@ -436,6 +444,9 @@ public void readHeader() throws IOException { } } length = ByteUtils.bytesToInt(buf, 4, bigEndian); + if (length < -1) { + throw tagValueTooLargeException(); + } } public boolean readItemHeader() throws IOException { @@ -776,7 +787,8 @@ public byte[] readValue() throws IOException { int valLen = length; try { if (valLen < 0) - throw new EOFException(); // assume InputStream length < 2 GiB + throw new IOException( + "internal error: length should have been validated in readHeader"); int allocLen = allocateLimit >= 0 ? Math.min(valLen, allocateLimit) : valLen; diff --git a/dcm4che-core/src/test/java/org/dcm4che3/io/DicomInputStreamTest.java b/dcm4che-core/src/test/java/org/dcm4che3/io/DicomInputStreamTest.java index dcd8e9d8c8..149e13d91e 100644 --- a/dcm4che-core/src/test/java/org/dcm4che3/io/DicomInputStreamTest.java +++ b/dcm4che-core/src/test/java/org/dcm4che3/io/DicomInputStreamTest.java @@ -42,6 +42,15 @@ public void testImplicitVR() throws Exception { assertEquals(1, attrs.getInt(Tag.SamplesPerPixel, 0)); } + @Test + public void testBulkDataLimit() throws Exception { + try { + readFrom("3gb-bulk-data-truncated", IncludeBulkData.URI); + } catch (DicomStreamException e) { + assertEquals("0x7FE00010 " + DicomInputStream.VALUE_TOO_LARGE, e.getMessage()); + } + } + @Test public void testSpoolDataFragments() throws Exception { List bulkDataFiles; diff --git a/dcm4che-test-data/src/main/resources/3gb-bulk-data-truncated b/dcm4che-test-data/src/main/resources/3gb-bulk-data-truncated new file mode 100644 index 0000000000..ae71f01c05 Binary files /dev/null and b/dcm4che-test-data/src/main/resources/3gb-bulk-data-truncated differ diff --git a/dcm4che-test-data/src/main/resources/META-INF/maven/remote-resources.xml b/dcm4che-test-data/src/main/resources/META-INF/maven/remote-resources.xml index 2f164b5453..bce14e4104 100644 --- a/dcm4che-test-data/src/main/resources/META-INF/maven/remote-resources.xml +++ b/dcm4che-test-data/src/main/resources/META-INF/maven/remote-resources.xml @@ -59,6 +59,7 @@ US-RGB-8-esopecho YBR_422.dcm YBR_FULL-RLE.dcm + 3gb-bulk-data-truncated UTF-8