ome / bioformats

Bio-Formats is a Java library for reading and writing data in life sciences image file formats. It is developed by the Open Microscopy Environment. Bio-Formats is released under the GNU General Public License (GPL); commercial licenses are available from Glencoe Software.
https://www.openmicroscopy.org/bio-formats
GNU General Public License v2.0
381 stars 241 forks source link

Infinite loop in MicromanagerReader #4018

Closed aherbert closed 1 year ago

aherbert commented 1 year ago

The MicromanagerReader can swallow an IOException and infinite loop. The offending method is:

private void parsePosition(int posIndex) throws IOException, FormatException

I have a TIFF file that has an incorrect IFD (due to file truncation). When the code executes int nIFDs = parser.getMainIFDs().size() the method getMainIFDs throws an EOFException. This is caught within the loop and logged. But the loop iteration counter plane is not incremented. Thus the loop just repeats. Reproduce with:

> showinf -version
Version: 6.13.0
Build date: 2 May 2023
VCS revision: afaff5ef177c51d99e673cad6031d31637b431eb

> showinf -nopix -debug mrc5\ 60_17_MMStack_Pos0.ome.tif

Debug output is:

...
Initializing reader
Location.mapFile: embedded-stream.raw -> null
Location.mapFile: embedded-stream.raw -> null
MicromanagerReader initializing mrc5 60_17_MMStack_Pos0.ome.tif
MicromanagerReader initializing mrc5 60_17_MMStack_Pos0.ome.tif
loci.formats.in.MicromanagerReader.initFile(mrc5 60_17_MMStack_Pos0.ome.tif)
Reading metadata file
Populating metadata
Finding image file names
Building list of TIFFs
Failed to read metadata from /data/images/laura/test/mrc5 60_17_MMStack_Pos0.ome.tif
java.io.EOFException: Attempting to read beyond end of file.
        at loci.common.NIOFileHandle.readShort(NIOFileHandle.java:452)
        at loci.common.NIOFileHandle.readUnsignedShort(NIOFileHandle.java:467)
        at loci.common.RandomAccessInputStream.readUnsignedShort(RandomAccessInputStream.java:658)
        at loci.formats.tiff.TiffParser.readTiffIFDEntry(TiffParser.java:1422)
        at loci.formats.tiff.TiffParser.getIFD(TiffParser.java:464)
        at loci.formats.tiff.TiffParser.getMainIFDs(TiffParser.java:243)
        at loci.formats.in.MicromanagerReader.parsePosition(MicromanagerReader.java:496)
        at loci.formats.in.MicromanagerReader.initFile(MicromanagerReader.java:308)
        at loci.formats.FormatReader.setId(FormatReader.java:1466)
        at loci.formats.ImageReader.setId(ImageReader.java:863)
        at loci.formats.ReaderWrapper.setId(ReaderWrapper.java:660)
        at loci.formats.tools.ImageInfo.testRead(ImageInfo.java:1043)
        at loci.formats.tools.ImageInfo.main(ImageInfo.java:1129)
Caused by: java.nio.BufferUnderflowException: null
        at java.base/java.nio.Buffer.nextGetIndex(Buffer.java:651)
        at java.base/java.nio.HeapByteBuffer.getShort(HeapByteBuffer.java:350)
        at loci.common.NIOFileHandle.readShort(NIOFileHandle.java:450)
        ... 12 common frames omitted
...
(Output then loops starting at 'Failed to read metadata from')

If I remove the sidecar metadata.txt file written by MicroManager then Bioformats selects the TiffReader. This trickles up the EOFException and the user is notified that there is an error with the image.

This bug was identified during an import into OMERO which just hangs due to the infinite loop.

Image to reproduce the error is attached:

mrc5 60_17_MMStack_Pos0.zip

A simple fix is to increment plane in the catch block. But it is not clear why it is acceptable to swallow an IOException for this format inside the loop. The call to private void parsePosition(String jsonData, int posIndex) outside the loop is allowed to throw an IOException.

dgault commented 1 year ago

Thanks @aherbert for reporting this and debugging the issue. I will do some digging into the reader history to see why the exception is only ever being logged, really an IOException like that should really be thrown. Hopefully this should be something we can quickly resolve in an upcoming release.