File CVE-2025-9732.patch of Package dcmtk
From fb4de6d353b62c60163317833375d7565287375f Mon Sep 17 00:00:00 2001
From: Joerg Riesmeier <dicom@jriesmeier.com>
Date: Fri, 15 Aug 2025 13:35:40 +0200
Subject: [PATCH] Fixed issue with invalid "YBR_FULL" DICOM images.
Fixed an issue when processing an invalid DICOM image with a Photometric
Interpretation of "YBR_FULL" and a Planar Configuration of "1" where
the number of pixels stored does not match the expected number of pixels
(much too less). Now, the pixel data of such an image is not processed
at all, but an empty image (black pixels) is created instead. The user
is warned about this by an appropriate log message.
Thanks to Ding zhengzheng <xiaozheng.ding399@gmail.com> for the report
and the sample file (PoC).
---
dcmimage/include/dcmtk/dcmimage/dicopxt.h | 6 +-
dcmimage/include/dcmtk/dcmimage/diybrpxt.h | 295 +++++++++++----------
dcmimgle/libsrc/dcmimage.cc | 3 +-
3 files changed, 160 insertions(+), 144 deletions(-)
diff --git a/dcmimage/include/dcmtk/dcmimage/dicopxt.h b/dcmimage/include/dcmtk/dcmimage/dicopxt.h
index d812d16..d0ce15d 100644
--- a/dcmimage/include/dcmtk/dcmimage/dicopxt.h
+++ b/dcmimage/include/dcmtk/dcmimage/dicopxt.h
@@ -574,7 +574,11 @@ class DiColorPixelTemplate
{
/* erase empty part of the buffer (=blacken the background) */
if (InputCount < Count)
- OFBitmanipTemplate<T>::zeroMem(Data[j] + InputCount, Count - InputCount);
+ {
+ const size_t count = (Count - InputCount);
+ DCMIMAGE_TRACE("filing empty part of the intermediate pixel data (" << count << " pixels) of plane " << j << " with value = 0");
+ OFBitmanipTemplate<T>::zeroMem(Data[j] + InputCount, count);
+ }
} else {
DCMIMAGE_DEBUG("cannot allocate memory buffer for 'Data[" << j << "]' in DiColorPixelTemplate::Init()");
result = 0; // at least one buffer could not be allocated!
diff --git a/dcmimage/include/dcmtk/dcmimage/diybrpxt.h b/dcmimage/include/dcmtk/dcmimage/diybrpxt.h
index 9655729..c5415c1 100644
--- a/dcmimage/include/dcmtk/dcmimage/diybrpxt.h
+++ b/dcmimage/include/dcmtk/dcmimage/diybrpxt.h
@@ -1,6 +1,6 @@
/*
*
- * Copyright (C) 1998-2016, OFFIS e.V.
+ * Copyright (C) 1998-2025, OFFIS e.V.
* All rights reserved. See COPYRIGHT file for details.
*
* This software and supporting documentation were developed by
@@ -24,6 +24,7 @@
#define DIYBRPXT_H
#include "dcmtk/config/osconfig.h"
+#include "dcmtk/ofstd/ofbmanip.h"
#include "dcmtk/dcmimage/dicopxt.h"
#include "dcmtk/dcmimgle/diinpx.h" /* gcc 3.4 needs this */
@@ -90,179 +91,189 @@ class DiYBRPixelTemplate
// use the number of input pixels derived from the length of the 'PixelData'
// attribute), but not more than the size of the intermediate buffer
const unsigned long count = (this->InputCount < this->Count) ? this->InputCount : this->Count;
- if (rgb) /* convert to RGB model */
+ // make sure that there is sufficient input data (for planar pixel data)
+ if (!this->PlanarConfiguration || (count >= planeSize * 3 /* number of planes */))
{
- T2 *r = this->Data[0];
- T2 *g = this->Data[1];
- T2 *b = this->Data[2];
- const T2 maxvalue = OFstatic_cast(T2, DicomImageClass::maxval(bits));
- DiPixelRepresentationTemplate<T1> rep;
- if (bits == 8 && !rep.isSigned()) // only for unsigned 8 bit
+ if (rgb) /* convert to RGB model */
{
- Sint16 rcr_tab[256];
- Sint16 gcb_tab[256];
- Sint16 gcr_tab[256];
- Sint16 bcb_tab[256];
- const double r_const = 0.7010 * OFstatic_cast(double, maxvalue);
- const double g_const = 0.5291 * OFstatic_cast(double, maxvalue);
- const double b_const = 0.8859 * OFstatic_cast(double, maxvalue);
- unsigned long l;
- for (l = 0; l < 256; ++l)
+ T2 *r = this->Data[0];
+ T2 *g = this->Data[1];
+ T2 *b = this->Data[2];
+ const T2 maxvalue = OFstatic_cast(T2, DicomImageClass::maxval(bits));
+ DiPixelRepresentationTemplate<T1> rep;
+ if (bits == 8 && !rep.isSigned()) // only for unsigned 8 bit
{
- rcr_tab[l] = OFstatic_cast(Sint16, 1.4020 * OFstatic_cast(double, l) - r_const);
- gcb_tab[l] = OFstatic_cast(Sint16, 0.3441 * OFstatic_cast(double, l));
- gcr_tab[l] = OFstatic_cast(Sint16, 0.7141 * OFstatic_cast(double, l) - g_const);
- bcb_tab[l] = OFstatic_cast(Sint16, 1.7720 * OFstatic_cast(double, l) - b_const);
- }
- Sint32 sr;
- Sint32 sg;
- Sint32 sb;
- if (this->PlanarConfiguration)
- {
-/*
- const T1 *y = pixel;
- const T1 *cb = y + this->InputCount;
- const T1 *cr = cb + this->InputCount;
- for (i = count; i != 0; --i, ++y, ++cb, ++cr)
+ Sint16 rcr_tab[256];
+ Sint16 gcb_tab[256];
+ Sint16 gcr_tab[256];
+ Sint16 bcb_tab[256];
+ const double r_const = 0.7010 * OFstatic_cast(double, maxvalue);
+ const double g_const = 0.5291 * OFstatic_cast(double, maxvalue);
+ const double b_const = 0.8859 * OFstatic_cast(double, maxvalue);
+ unsigned long l;
+ for (l = 0; l < 256; ++l)
{
- sr = OFstatic_cast(Sint32, *y) + OFstatic_cast(Sint32, rcr_tab[*cr]);
- sg = OFstatic_cast(Sint32, *y) - OFstatic_cast(Sint32, gcb_tab[*cb]) - OFstatic_cast(Sint32, gcr_tab[*cr]);
- sb = OFstatic_cast(Sint32, *y) + OFstatic_cast(Sint32, bcb_tab[*cb]);
- *(r++) = (sr < 0) ? 0 : (sr > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sr);
- *(g++) = (sg < 0) ? 0 : (sg > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sg);
- *(b++) = (sb < 0) ? 0 : (sb > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sb);
+ rcr_tab[l] = OFstatic_cast(Sint16, 1.4020 * OFstatic_cast(double, l) - r_const);
+ gcb_tab[l] = OFstatic_cast(Sint16, 0.3441 * OFstatic_cast(double, l));
+ gcr_tab[l] = OFstatic_cast(Sint16, 0.7141 * OFstatic_cast(double, l) - g_const);
+ bcb_tab[l] = OFstatic_cast(Sint16, 1.7720 * OFstatic_cast(double, l) - b_const);
}
+ Sint32 sr;
+ Sint32 sg;
+ Sint32 sb;
+ if (this->PlanarConfiguration)
+ {
+/*
+ const T1 *y = pixel;
+ const T1 *cb = y + this->InputCount;
+ const T1 *cr = cb + this->InputCount;
+ for (i = count; i != 0; --i, ++y, ++cb, ++cr)
+ {
+ sr = OFstatic_cast(Sint32, *y) + OFstatic_cast(Sint32, rcr_tab[*cr]);
+ sg = OFstatic_cast(Sint32, *y) - OFstatic_cast(Sint32, gcb_tab[*cb]) - OFstatic_cast(Sint32, gcr_tab[*cr]);
+ sb = OFstatic_cast(Sint32, *y) + OFstatic_cast(Sint32, bcb_tab[*cb]);
+ *(r++) = (sr < 0) ? 0 : (sr > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sr);
+ *(g++) = (sg < 0) ? 0 : (sg > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sg);
+ *(b++) = (sb < 0) ? 0 : (sb > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sb);
+ }
*/
- const T1 *y = pixel;
- const T1 *cb = y + planeSize;
- const T1 *cr = cb + planeSize;
- unsigned long i = count;
- while (i != 0)
+ const T1 *y = pixel;
+ const T1 *cb = y + planeSize;
+ const T1 *cr = cb + planeSize;
+ unsigned long i = count;
+ while (i != 0)
+ {
+ /* convert a single frame */
+ for (l = planeSize; (l != 0) && (i != 0); --l, --i, ++y, ++cb, ++cr)
+ {
+ sr = OFstatic_cast(Sint32, *y) + OFstatic_cast(Sint32, rcr_tab[*cr]);
+ sg = OFstatic_cast(Sint32, *y) - OFstatic_cast(Sint32, gcb_tab[*cb]) - OFstatic_cast(Sint32, gcr_tab[*cr]);
+ sb = OFstatic_cast(Sint32, *y) + OFstatic_cast(Sint32, bcb_tab[*cb]);
+ *(r++) = (sr < 0) ? 0 : (sr > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sr);
+ *(g++) = (sg < 0) ? 0 : (sg > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sg);
+ *(b++) = (sb < 0) ? 0 : (sb > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sb);
+ }
+ /* jump to next frame start (skip 2 planes) */
+ y += 2 * planeSize;
+ cb += 2 * planeSize;
+ cr += 2 * planeSize;
+ }
+ }
+ else
{
- /* convert a single frame */
- for (l = planeSize; (l != 0) && (i != 0); --l, --i, ++y, ++cb, ++cr)
+ const T1 *p = pixel;
+ T1 y;
+ T1 cb;
+ T1 cr;
+ unsigned long i;
+ for (i = count; i != 0; --i)
{
- sr = OFstatic_cast(Sint32, *y) + OFstatic_cast(Sint32, rcr_tab[OFstatic_cast(Uint32, *cr)]);
- sg = OFstatic_cast(Sint32, *y) - OFstatic_cast(Sint32, gcb_tab[OFstatic_cast(Uint32, *cb)]) - OFstatic_cast(Sint32, gcr_tab[OFstatic_cast(Uint32, *cr)]);
- sb = OFstatic_cast(Sint32, *y) + OFstatic_cast(Sint32, bcb_tab[OFstatic_cast(Uint32, *cb)]);
+ y = *(p++);
+ cb = *(p++);
+ cr = *(p++);
+ sr = OFstatic_cast(Sint32, y) + OFstatic_cast(Sint32, rcr_tab[cr]);
+ sg = OFstatic_cast(Sint32, y) - OFstatic_cast(Sint32, gcb_tab[cb]) - OFstatic_cast(Sint32, gcr_tab[cr]);
+ sb = OFstatic_cast(Sint32, y) + OFstatic_cast(Sint32, bcb_tab[cb]);
*(r++) = (sr < 0) ? 0 : (sr > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sr);
*(g++) = (sg < 0) ? 0 : (sg > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sg);
*(b++) = (sb < 0) ? 0 : (sb > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sb);
}
- /* jump to next frame start (skip 2 planes) */
- y += 2 * planeSize;
- cb += 2 * planeSize;
- cr += 2 * planeSize;
}
}
else
{
- const T1 *p = pixel;
- T1 y;
- T1 cb;
- T1 cr;
- unsigned long i;
- for (i = count; i != 0; --i)
+ if (this->PlanarConfiguration)
+ {
+/*
+ const T1 *y = pixel;
+ const T1 *cb = y + this->InputCount;
+ const T1 *cr = cb + this->InputCount;
+ for (i = count; i != 0; --i)
+ convertValue(*(r++), *(g++), *(b++), removeSign(*(y++), offset), removeSign(*(cb++), offset),
+ removeSign(*(cr++), offset), maxvalue);
+*/
+ unsigned long l;
+ unsigned long i = count;
+ const T1 *y = pixel;
+ const T1 *cb = y + planeSize;
+ const T1 *cr = cb + planeSize;
+ while (i != 0)
+ {
+ /* convert a single frame */
+ for (l = planeSize; (l != 0) && (i != 0); --l, --i)
+ {
+ convertValue(*(r++), *(g++), *(b++), removeSign(*(y++), offset), removeSign(*(cb++), offset),
+ removeSign(*(cr++), offset), maxvalue);
+ }
+ /* jump to next frame start (skip 2 planes) */
+ y += 2 * planeSize;
+ cb += 2 * planeSize;
+ cr += 2 * planeSize;
+ }
+ }
+ else
{
- y = *(p++);
- cb = *(p++);
- cr = *(p++);
- sr = OFstatic_cast(Sint32, y) + OFstatic_cast(Sint32, rcr_tab[OFstatic_cast(Uint32, cr)]);
- sg = OFstatic_cast(Sint32, y) - OFstatic_cast(Sint32, gcb_tab[OFstatic_cast(Uint32, cb)]) - OFstatic_cast(Sint32, gcr_tab[OFstatic_cast(Uint32, cr)]);
- sb = OFstatic_cast(Sint32, y) + OFstatic_cast(Sint32, bcb_tab[OFstatic_cast(Uint32, cb)]);
- *(r++) = (sr < 0) ? 0 : (sr > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sr);
- *(g++) = (sg < 0) ? 0 : (sg > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sg);
- *(b++) = (sb < 0) ? 0 : (sb > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sb);
+ const T1 *p = pixel;
+ T2 y;
+ T2 cb;
+ T2 cr;
+ unsigned long i;
+ for (i = count; i != 0; --i)
+ {
+ y = removeSign(*(p++), offset);
+ cb = removeSign(*(p++), offset);
+ cr = removeSign(*(p++), offset);
+ convertValue(*(r++), *(g++), *(b++), y, cb, cr, maxvalue);
+ }
}
}
- }
- else
- {
+ } else { /* retain YCbCr model */
+ const T1 *p = pixel;
if (this->PlanarConfiguration)
{
-/*
- const T1 *y = pixel;
- const T1 *cb = y + this->InputCount;
- const T1 *cr = cb + this->InputCount;
- for (i = count; i != 0; --i)
- convertValue(*(r++), *(g++), *(b++), removeSign(*(y++), offset), removeSign(*(cb++), offset),
- removeSign(*(cr++), offset), maxvalue);
-*/
+ /*
+ T2 *q;
+ // number of pixels to be skipped (only applicable if 'PixelData' contains more
+ // pixels than expected)
+ const unsigned long skip = (this->InputCount > this->Count) ? (this->InputCount - this->Count) : 0;
+ for (int j = 0; j < 3; ++j)
+ {
+ q = this->Data[j];
+ for (i = count; i != 0; --i)
+ *(q++) = removeSign(*(p++), offset);
+ // skip to beginning of next plane
+ p += skip;
+ }
+ */
unsigned long l;
- unsigned long i = count;
- const T1 *y = pixel;
- const T1 *cb = y + planeSize;
- const T1 *cr = cb + planeSize;
- while (i != 0)
+ unsigned long i = 0;
+ while (i < count)
{
- /* convert a single frame */
- for (l = planeSize; (l != 0) && (i != 0); --l, --i)
+ /* store current pixel index */
+ const unsigned long iStart = i;
+ for (int j = 0; j < 3; ++j)
{
- convertValue(*(r++), *(g++), *(b++), removeSign(*(y++), offset), removeSign(*(cb++), offset),
- removeSign(*(cr++), offset), maxvalue);
+ /* convert a single plane */
+ for (l = planeSize, i = iStart; (l != 0) && (i < count); --l, ++i)
+ this->Data[j][i] = removeSign(*(p++), offset);
}
- /* jump to next frame start (skip 2 planes) */
- y += 2 * planeSize;
- cb += 2 * planeSize;
- cr += 2 * planeSize;
}
}
else
{
- const T1 *p = pixel;
- T2 y;
- T2 cb;
- T2 cr;
+ int j;
unsigned long i;
- for (i = count; i != 0; --i)
- {
- y = removeSign(*(p++), offset);
- cb = removeSign(*(p++), offset);
- cr = removeSign(*(p++), offset);
- convertValue(*(r++), *(g++), *(b++), y, cb, cr, maxvalue);
- }
+ for (i = 0; i < count; ++i) /* for all pixel ... */
+ for (j = 0; j < 3; ++j)
+ this->Data[j][i] = removeSign(*(p++), offset); /* ... copy planes */
}
}
- } else { /* retain YCbCr model */
- const T1 *p = pixel;
- if (this->PlanarConfiguration)
- {
-/*
- T2 *q;
- // number of pixels to be skipped (only applicable if 'PixelData' contains more
- // pixels than expected)
- const unsigned long skip = (this->InputCount > this->Count) ? (this->InputCount - this->Count) : 0;
- for (int j = 0; j < 3; ++j)
- {
- q = this->Data[j];
- for (i = count; i != 0; --i)
- *(q++) = removeSign(*(p++), offset);
- // skip to beginning of next plane
- p += skip;
- }
-*/
- unsigned long l;
- unsigned long i = 0;
- while (i < count)
- {
- /* store current pixel index */
- const unsigned long iStart = i;
- for (int j = 0; j < 3; ++j)
- {
- /* convert a single plane */
- for (l = planeSize, i = iStart; (l != 0) && (i < count); --l, ++i)
- this->Data[j][i] = removeSign(*(p++), offset);
- }
- }
- }
- else
- {
- int j;
- unsigned long i;
- for (i = 0; i < count; ++i) /* for all pixel ... */
- for (j = 0; j < 3; ++j)
- this->Data[j][i] = removeSign(*(p++), offset); /* ... copy planes */
- }
+ } else {
+ // do not process the input data, as it is too short
+ DCMIMAGE_WARN("input data is too short, filling the complete image with black pixels");
+ // erase empty part of the buffer (that has not been "blackened" yet)
+ for (int j = 0; j < 3; ++j)
+ OFBitmanipTemplate<T2>::zeroMem(this->Data[j], count);
}
}
}
diff --git a/dcmimgle/libsrc/dcmimage.cc b/dcmimgle/libsrc/dcmimage.cc
index bc395a2..ed10726 100644
--- a/dcmimgle/libsrc/dcmimage.cc
+++ b/dcmimgle/libsrc/dcmimage.cc
@@ -1,6 +1,6 @@
/*
*
- * Copyright (C) 1996-2024, OFFIS e.V.
+ * Copyright (C) 1996-2025, OFFIS e.V.
* All rights reserved. See COPYRIGHT file for details.
*
* This software and supporting documentation were developed by
@@ -210,6 +210,7 @@ void DicomImage::Init()
*(q++) = c;
}
*q = '\0'; // end of C string
+ DCMIMGLE_DEBUG("filtered version of 'PhotometricInterpretation' = " << OFSTRING_GUARD(cstr));
while ((pin->Name != NULL) && (strcmp(pin->Name, cstr) != 0))
++pin;
delete[] cstr;
--
2.51.0