Fixed issue with invalid "YBR_FULL" DICOM images.
authorJoerg Riesmeier <dicom@jriesmeier.com>
Wed, 10 Dec 2025 21:34:17 +0000 (22:34 +0100)
committerÉtienne Mollier <emollier@debian.org>
Wed, 10 Dec 2025 21:34:17 +0000 (22:34 +0100)
Applied-Upstream: 7ad81d69b19714936e18ea5fc74edaeb9f021ce7
Reviewed-By: Étienne Mollier <emollier@debian.org>
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 <xiaozheng.ding399@gmail.com> for the report
and the sample file (PoC).

Gbp-Pq: Name 0013-CVE-2025-9732.patch

dcmimage/include/dcmtk/dcmimage/dicopxt.h
dcmimage/include/dcmtk/dcmimage/diybrpxt.h
dcmimgle/libsrc/dcmimage.cc

index d812d169ee318bea6713945b176f822859a902ec..d0ce15d67b9d7885b3afa029d80ea204fd090cea 100644 (file)
@@ -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!
index 965572977ef21963c60f68f2a90b1cce0d838a4e..c5415c149729ef1bd31e3cf02746d386e29c5c7a 100644 (file)
@@ -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);
             }
         }
     }
index bc395a2792ad75500a4425aafdfaf95f047bd1db..ed107264c41de8f51972ae71ae775ce73448d67c 100644 (file)
@@ -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;