Bug #1196 » IC-DCMTK-0008_REPORT.md
IC-DCMTK-0008: SEGV via OOB Read in DcmJSONReader getTokenContent
Version: DCMTK master 418274445 (DCMTK-3.7.0+64)
CWE: CWE-125 (Out-of-bounds Read)
Description
A segmentation fault exists in DcmJSONReader::getTokenContent() (dcmdata/libsrc/dcjsonrd.cc:221) caused by the same JSMN two-pass token mismatch as IC-DCMTK-0006, but with a distinct crash behavior: the out-of-bounds read extends into unmapped virtual memory pages, producing a hard SIGSEGV regardless of whether AddressSanitizer is enabled. A 25-byte malformed JSON input triggers the crash.
The root cause is the JSMN two-pass token count discrepancy. Pass 1 counts tokens without bracket-match validation; pass 2 validates and returns JSMN_ERROR_INVAL on mismatch. The error is swallowed at dcjsonrd.cc:1204 when stopOnErrorPolicy_ is false. The resulting token array contains entries with corrupted start/end values. Unlike IC-DCMTK-0006 where the OOB read lands in adjacent heap memory (and might silently succeed without ASan), this variant's token offsets compute to an address in unmapped pages, causing an unconditional SIGSEGV.
The specific trigger -- missing colons between JSON keys and values -- causes JSMN to produce tokens whose start/end values sum to an offset far past the 25-byte buffer:
// dcjsonrd.cc:216-231
void DcmJSONReader::getTokenContent(OFString& value, OFJsmnTokenPtr t)
{
int size = t->end - t->start;
char c = jsonDataset_[t->start+size]; // LINE 221: OOB into unmapped page → SIGSEGV
jsonDataset_[t->start+size] = '\0'; // OOB WRITE
value = jsonDataset_ + t->start;
jsonDataset_[t->start+size] = c; // OOB WRITE
return;
}
Reproduction
# Using the included poc.json (25 bytes, AFL++ fuzzer-generated), or:
export DCMDICTPATH=/path/to/dcmtk/dcmdata/data/dicom.dic
./bin/json2dcm --ignore-errors poc.json /dev/null
Without ASan:
Segmentation fault (core dumped)
With ASan:
E: parse error in JSON file
E: not a valid DICOM JSON dataset: attribute tag must consist of two 16-bit hex numbers
AddressSanitizer:DEADLYSIGNAL
=================================================================
==2187201==ERROR: AddressSanitizer: SEGV on unknown address 0x7cd076ee003f (pc 0x555555787614 bp 0x7fffffffc3f0 sp 0x7fffffffc2f0 T0)
==2187201==The signal is caused by a READ memory access.
#0 0x555555787614 in DcmJSONReader::getTokenContent(OFString&, jsmntok*) dcmdata/libsrc/dcjsonrd.cc:221:14
#1 0x5555557908ef in DcmJSONReader::parseElement(DcmItem*, DcmItem*, jsmntok*&) dcmdata/libsrc/dcjsonrd.cc:642:9
#2 0x555555797e03 in DcmJSONReader::parseDataSet(DcmItem*, DcmItem*, jsmntok*&) dcmdata/libsrc/dcjsonrd.cc:923:18
#3 0x5555557a19c7 in DcmJSONReader::readAndConvertJSONFile(DcmFileFormat&, char const*) dcmdata/libsrc/dcjsonrd.cc:1279:18
#4 0x55555571339b in main dcmdata/apps/json2dcm.cc:435:25
#5 0x7ffff7989082 in __libc_start_main /build/glibc-LcI20x/glibc-2.31/csu/../csu/libc-start.c:308:16
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV dcmdata/libsrc/dcjsonrd.cc:221:14 in DcmJSONReader::getTokenContent(OFString&, jsmntok*)
==2187201==ABORTING
Fix
Add bounds validation in getTokenContent() to reject tokens with invalid or out-of-range boundaries:
void DcmJSONReader::getTokenContent(OFString& value, OFJsmnTokenPtr t)
{
int size = t->end - t->start;
if (size < 0 || t->start < 0 || (size_t)(t->start + size) >= jsonDatasetLen_)
{
value.clear();
return; // reject invalid token boundaries
}
char c = jsonDataset_[t->start+size];
jsonDataset_[t->start+size] = '\0';
value = jsonDataset_ + t->start;
jsonDataset_[t->start+size] = c;
}
- « Previous
- 1
- 2
- 3
- Next »