Project

General

Profile

Bug #1190 » IC-DCMTK-0004_REPORT.md

Jörg Riesmeier, 2026-03-10 19:21

 

IC-DCMTK-0004: Infinite Loop in JPEG Segment Parser (TEM Marker)

Version: DCMTK master 418274445 (DCMTK-3.7.0+64)
CWE: CWE-835 (Loop with Unreachable Exit Condition)

Description

The function DJCodecDecoder::scanJpegDataForBitDepth() in dcmjpeg/libsrc/djcodecd.cc iterates through JPEG segment markers to determine the bit depth of compressed pixel data. The function handles over 30 different JPEG marker types in a while loop, and each case is expected to advance the offset variable so the loop makes progress. However, the TEM marker (0xFF01) case at line 852 contains only a break statement without advancing the offset, causing the parser to re-read the same TEM marker indefinitely.

A 526-byte DICOM file with an embedded JPEG stream containing a TEM marker causes any DCMTK application that attempts JPEG decompression to hang permanently at 100% CPU. The TEM (Temporary) marker is a standalone marker with no payload defined in ITU-T T.81, section B.2.4.6 -- it should advance by 2 bytes just like SOI, EOI, and RST markers.

// djcodecd.cc:719-872 (simplified)
while(offset+4 < fragmentLength)
{
    switch(readUint16(data+offset))
    {
      case 0xffc0: return data[offset+4];   // SOF -- returns bit depth
      case 0xffc4: offset += readUint16(data+offset+2)+2; break; // DHT -- advances
      case 0xffd0: case 0xffd1: /* ... */ case 0xffd7:
        offset += 2; break;                 // RST -- advances
      case 0xffd8: offset += 2; break;      // SOI -- advances
      case 0xffd9: offset += 2; break;      // EOI -- advances
      // ... 20+ more cases, ALL advance offset ...
      case 0xff01: // TEM
        break;     // BUG: offset is NOT advanced -- infinite loop!
      default: offset += 2; break;
    }
}

The function is called from DJCodecDecoder::decode() at line 165 during any JPEG decompression operation, including dcmdjpeg, DicomImage construction, and DcmDataset::chooseRepresentation().

Reproduction

# poc.dcm is a 526-byte DICOM file with JPEG Baseline transfer syntax
# containing FF D8 (SOI) followed by FF 01 (TEM)
timeout 10 ./bin/dcmdjpeg poc.dcm /dev/null
# Exit code 124 = killed by timeout (confirms infinite loop)

Actual output:

$ timeout 10 dcmdjpeg poc.dcm /dev/null
W: DcmItem: Dataset not in ascending tag order, at element (0028,0008)
<process hangs at 100% CPU -- killed by timeout>

Exit code: 124 (killed by timeout after 10 seconds)

The process enters an infinite loop in scanJpegDataForBitDepth()
at the TEM marker (0xFF01) handler. The 'case 0xe1' (TEM) breaks
without advancing the read offset, causing the same marker to be
re-read indefinitely at 100% CPU.

Fix

Handle TEM (0xFF01) as a standalone marker by advancing past it without entering the marker-length parsing path:

case 0xff01: // TEM
    offset += 2;  // TEM is a standalone marker, advance past it
    break;
(3-3/3)