From: Joerg Riesmeier Date: Wed, 10 Dec 2025 21:34:17 +0000 (+0100) Subject: Fixed issue with invalid "YBR_FULL" DICOM images. X-Git-Tag: archive/raspbian/3.6.9-6+rpi1^2~2 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=9ce51ee9186165cd8d059bd72521a4bc8fc14014;p=dcmtk.git Fixed issue with invalid "YBR_FULL" DICOM images. Applied-Upstream: 7ad81d69b19714936e18ea5fc74edaeb9f021ce7 Reviewed-By: Étienne Mollier Last-Update: 2025-08-15 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 for the report and the sample file (PoC). Gbp-Pq: Name 0013-CVE-2025-9732.patch --- diff --git a/dcmimage/include/dcmtk/dcmimage/dicopxt.h b/dcmimage/include/dcmtk/dcmimage/dicopxt.h index d812d169..d0ce15d6 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::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::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 96557297..c5415c14 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 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 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::zeroMem(this->Data[j], count); } } } diff --git a/dcmimgle/libsrc/dcmimage.cc b/dcmimgle/libsrc/dcmimage.cc index bc395a27..ed107264 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;