[med-svn] [Git][med-team/dcmtk][debian/bookworm] 8 commits: 0012-CVE-2022-4981.patch: new: fix CVE-2022-4981.
Étienne Mollier (@emollier)
gitlab at salsa.debian.org
Sat Jun 13 22:02:54 BST 2026
Étienne Mollier pushed to branch debian/bookworm at Debian Med / dcmtk
Commits:
e4a751e1 by Étienne Mollier at 2026-06-12T20:24:23+02:00
0012-CVE-2022-4981.patch: new: fix CVE-2022-4981.
- - - - -
f78b200e by Étienne Mollier at 2026-06-12T20:29:25+02:00
0013-CVE-2025-2357.patch: new: fix CVE-2025-2357.
Closes: #1100724
- - - - -
145708c8 by Étienne Mollier at 2026-06-12T20:33:56+02:00
*CVE-2025-9732*.patch: new.
These two patches fix CVE-2025-9732.
Closes: #1113993
- - - - -
b6ed269d by Étienne Mollier at 2026-06-12T20:36:51+02:00
0016-CVE-2025-14607.patch: new: fix CVE-2025-14607.
Closes: #1122926
- - - - -
de913db2 by Étienne Mollier at 2026-06-12T20:38:30+02:00
0017-CVE-2025-14841.patch: new: fix CVE-2025-14841.
Closes: #1123584
- - - - -
6aa0897a by Étienne Mollier at 2026-06-12T23:15:36+02:00
0018-CVE-2026-5663.patch: new: fix CVE-2026-5663.
This patch required some rework from upstream due to little changes in
the logic and the coding style.
Closes: #1133001
- - - - -
526cfe00 by Étienne Mollier at 2026-06-12T23:15:41+02:00
0019-CVE-2026-10194.patch: new: fix CVE-2026-10194.
Closes: #1139181
- - - - -
b247b97c by Étienne Mollier at 2026-06-12T23:15:41+02:00
d/changelog: ready for upload to bookworm.
- - - - -
10 changed files:
- debian/changelog
- + debian/patches/0012-CVE-2022-4981.patch
- + debian/patches/0013-CVE-2025-2357.patch
- + debian/patches/0014-CVE-2025-9732.patch
- + debian/patches/0015-CVE-2025-9732b.patch
- + debian/patches/0016-CVE-2025-14607.patch
- + debian/patches/0017-CVE-2025-14841.patch
- + debian/patches/0018-CVE-2026-5663.patch
- + debian/patches/0019-CVE-2026-10194.patch
- debian/patches/series
Changes:
=====================================
debian/changelog
=====================================
@@ -1,3 +1,19 @@
+dcmtk (3.6.7-9~deb12u4) bookworm; urgency=medium
+
+ * Team upload.
+ * 0012-CVE-2022-4981.patch: new: fix CVE-2022-4981.
+ * 0013-CVE-2025-2357.patch: new: fix CVE-2025-2357. (Closes: #1100724)
+ * *CVE-2025-9732*.patch: new.
+ These two patches fix CVE-2025-9732. (Closes: #1113993)
+ * 0016-CVE-2025-14607.patch: new: fix CVE-2025-14607. (Closes: #1122926)
+ * 0017-CVE-2025-14841.patch: new: fix CVE-2025-14841. (Closes: #1123584)
+ * 0018-CVE-2026-5663.patch: new: fix CVE-2026-5663.
+ This patch required some rework from upstream due to little changes in
+ the logic and the coding style. (Closes: #1133001)
+ * 0019-CVE-2026-10194.patch: new: fix CVE-2026-10194. (Closes: #1139181)
+
+ -- Étienne Mollier <emollier at debian.org> Fri, 12 Jun 2026 20:59:14 +0200
+
dcmtk (3.6.7-9~deb12u3) bookworm; urgency=medium
* Team upload.
=====================================
debian/patches/0012-CVE-2022-4981.patch
=====================================
@@ -0,0 +1,90 @@
+commit 957fb31e5d96f51ecf5cb3422c7dc2227f8e0423
+Author: Marco Eichelberg <dicom at offis.de>
+Date: Tue May 24 11:22:35 2022 +0200
+
+ Fixed dcmqrscp crash caused by malformed config file.
+
+ Fixed various issues in the dcmqrscp configuration file parser
+ that could cause application crashes when reading a malformed configuration
+ file, due to insufficient checks of the input data.
+
+ This closes DCMTK issue #1026.
+
+ Thanks to Zahra Mirzamomen <zahra.mirzamomen at monash.edu> and
+ Marcel Böhme <marcel.boehme at mpi-sp.org> for the bug report and sample files.
+
+diff --git a/dcmqrdb/libsrc/dcmqrcnf.cc b/dcmqrdb/libsrc/dcmqrcnf.cc
+index 686c9df2a..36a295fa2 100644
+--- a/dcmqrdb/libsrc/dcmqrcnf.cc
++++ b/dcmqrdb/libsrc/dcmqrcnf.cc
+@@ -1,6 +1,6 @@
+ /*
+ *
+- * Copyright (C) 1993-2021, OFFIS e.V.
++ * Copyright (C) 1993-2022, OFFIS e.V.
+ * All rights reserved. See COPYRIGHT file for details.
+ *
+ * This software and supporting documentation were developed by
+@@ -447,7 +447,7 @@ int DcmQueryRetrieveConfig::readHostTable(FILE *cnffp, int *lineno)
+ {
+ int error = 0, /* error flag */
+ end = 0, /* end flag */
+- noOfPeers; /* number of peers for entry */
++ noOfPeers=0; /* number of peers for entry */
+ char rcline[512], /* line in configuration file */
+ mnemonic[512], /* mnemonic in line */
+ value[512], /* parameter value */
+@@ -676,26 +676,34 @@ DcmQueryRetrieveConfigPeer *DcmQueryRetrieveConfig::readPeerList(char **valuehan
+ while((helpvalue = parsevalues(valuehandle)) != NULL) {
+ found = 0;
+ if (strchr(helpvalue, ',') == NULL) { /* symbolic name */
+- if (!CNF_HETable.noOfHostEntries) {
+- panic("No symbolic names defined");
+- *peers = 0;
+- free(helpvalue);
+- return((DcmQueryRetrieveConfigPeer *) 0);
+- }
+- for(i = 0; i < CNF_HETable.noOfHostEntries; i++) {
+- if (!strcmp(CNF_HETable.HostEntries[i].SymbolicName, helpvalue)) {
+- found = 1;
+- break;
+- }
+- }
+- if (!found) {
+- panic("Symbolic name \"%s\" not defined", helpvalue);
+- *peers = 0;
+- free(helpvalue);
+- return((DcmQueryRetrieveConfigPeer *) 0);
+- }
++ if (!CNF_HETable.noOfHostEntries) {
++ panic("No symbolic names defined");
++ *peers = 0;
++ free(helpvalue);
++ return((DcmQueryRetrieveConfigPeer *) 0);
++ }
++ for(i = 0; i < CNF_HETable.noOfHostEntries; i++) {
++ if ((CNF_HETable.HostEntries[i].SymbolicName != NULL) && (0 == strcmp(CNF_HETable.HostEntries[i].SymbolicName, helpvalue))) {
++ found = 1;
++ break;
++ }
++ }
++ if (!found) {
++ panic("Symbolic name \"%s\" not defined", helpvalue);
++ *peers = 0;
++ free(helpvalue);
++ return((DcmQueryRetrieveConfigPeer *) 0);
++ }
++
++ if (CNF_HETable.HostEntries[i].noOfPeers <= 0) {
++ panic("No peers for symbolic name \"%s\" defined", helpvalue);
++ *peers = 0;
++ free(helpvalue);
++ return((DcmQueryRetrieveConfigPeer *) 0);
++ }
++
++ noOfPeers += CNF_HETable.HostEntries[i].noOfPeers;
+
+- noOfPeers += CNF_HETable.HostEntries[i].noOfPeers;
+ if ((helppeer = (DcmQueryRetrieveConfigPeer *)malloc(noOfPeers * sizeof(DcmQueryRetrieveConfigPeer))) == NULL)
+ panic("Memory allocation 5 (%d)", noOfPeers);
+ if (noOfPeers - CNF_HETable.HostEntries[i].noOfPeers) {
=====================================
debian/patches/0013-CVE-2025-2357.patch
=====================================
@@ -0,0 +1,510 @@
+commit 3239a791542e1ea433d23aaa9e0a05a532ffabff
+Author: Marco Eichelberg <eichelberg at offis.de>
+Date: Mon Mar 3 12:33:18 2025 +0100
+
+ Fixed segfault in JPEG-LS decoder.
+
+ Fixed a bug in the JPEG-LS decoder that led to a segmentation fault if invalid
+ input data was processed, due to insufficient validation of input data.
+
+ Thanks to Ding zhengzheng <xiaozheng.ding399 at gmail.com> for the report
+ and the sample file (PoC).
+
+ This closes DCMTK issue #1155.
+
+diff --git a/dcmjpls/libcharls/scan.h b/dcmjpls/libcharls/scan.h
+index b4dea20d8..f13098104 100644
+--- a/dcmjpls/libcharls/scan.h
++++ b/dcmjpls/libcharls/scan.h
+@@ -1,6 +1,6 @@
+-//
+-// (C) Jan de Vaan 2007-2010, all rights reserved. See the accompanying "License.txt" for licensed use.
+-//
++//
++// (C) Jan de Vaan 2007-2010, all rights reserved. See the accompanying "License.txt" for licensed use.
++//
+
+ #ifndef CHARLS_SCAN
+ #define CHARLS_SCAN
+@@ -11,7 +11,7 @@
+
+ #include "lokuptbl.h"
+
+-// This file contains the code for handling a "scan". Usually an image is encoded as a single scan.
++// This file contains the code for handling a "scan". Usually an image is encoded as a single scan.
+
+ #include DCMTK_DIAGNOSTIC_IGNORE_CONST_EXPRESSION_WARNING
+
+@@ -21,10 +21,10 @@ extern OFVector<signed char> rgquant10Ll;
+ extern OFVector<signed char> rgquant12Ll;
+ extern OFVector<signed char> rgquant16Ll;
+ //
+-// Apply
++// Apply
+ //
+ inlinehint LONG ApplySign(LONG i, LONG sign)
+-{ return (sign ^ i) - sign; }
++{ return (sign ^ i) - sign; }
+
+
+
+@@ -58,20 +58,20 @@ inlinehint LONG GetPredictedValue(LONG Ra, LONG Rb, LONG Rc)
+
+ inlinehint LONG GetPredictedValue(LONG Ra, LONG Rb, LONG Rc)
+ {
+- // sign trick reduces the number of if statements (branches)
++ // sign trick reduces the number of if statements (branches)
+ LONG sgn = BitWiseSign(Rb - Ra);
+
+- // is Ra between Rc and Rb?
++ // is Ra between Rc and Rb?
+ if ((sgn ^ (Rc - Ra)) < 0)
+ {
+ return Rb;
+- }
++ }
+ else if ((sgn ^ (Rb - Rc)) < 0)
+ {
+ return Ra;
+ }
+
+- // default case, valid if Rc element of [Ra,Rb]
++ // default case, valid if Rc element of [Ra,Rb]
+ return Ra + Rb - Rc;
+ }
+
+@@ -110,7 +110,7 @@ public:
+
+ public:
+
+- JlsCodec(const TRAITS& inTraits, const JlsParameters& info) : STRATEGY(info),
++ JlsCodec(const TRAITS& inTraits, const JlsParameters& info) : STRATEGY(info),
+ traits(inTraits),
+ _rect(),
+ _width(0),
+@@ -120,13 +120,13 @@ public:
+ _RUNindex(0),
+ _pquant(0),
+ _bCompare(0)
+-
++
+ {
+ if (Info().ilv == ILV_NONE)
+ {
+ Info().components = 1;
+ }
+- }
++ }
+
+
+ void SetPresets(const JlsCustomParameters& presets)
+@@ -135,9 +135,9 @@ public:
+
+ InitParams(presets.T1 != 0 ? presets.T1 : presetDefault.T1,
+ presets.T2 != 0 ? presets.T2 : presetDefault.T2,
+- presets.T3 != 0 ? presets.T3 : presetDefault.T3,
++ presets.T3 != 0 ? presets.T3 : presetDefault.T3,
+ presets.RESET != 0 ? presets.RESET : presetDefault.RESET);
+- }
++ }
+
+
+ bool IsInterleaved()
+@@ -155,13 +155,13 @@ public:
+
+ signed char QuantizeGratientOrg(LONG Di);
+ inlinehint LONG QuantizeGratient(LONG Di)
+- {
++ {
+ ASSERT(QuantizeGratientOrg(Di) == *(_pquant + Di));
+- return *(_pquant + Di);
++ return *(_pquant + Di);
+ }
+
+ void InitQuantizationLUT();
+-
++
+ LONG DecodeValue(LONG k, LONG limit, LONG qbpp);
+ inlinehint void EncodeMappedValue(LONG k, LONG mappedError, LONG limit);
+
+@@ -216,27 +216,27 @@ public:
+ {
+ LONG sign = BitWiseSign(Qs);
+ JlsContext& ctx = _contexts[ApplySign(Qs, sign)];
+- LONG k = ctx.GetGolomb();
+- LONG Px = traits.CorrectPrediction(pred + ApplySign(ctx.C, sign));
++ LONG k = ctx.GetGolomb();
++ LONG Px = traits.CorrectPrediction(pred + ApplySign(ctx.C, sign));
+
+ LONG ErrVal;
+ const Code& code = decodingTables[k].Get(STRATEGY::PeekByte());
+ if (code.GetLength() != 0)
+ {
+ STRATEGY::Skip(code.GetLength());
+- ErrVal = code.GetValue();
++ ErrVal = code.GetValue();
+ ASSERT(ABS(ErrVal) < 65535);
+ }
+ else
+ {
+- ErrVal = UnMapErrVal(DecodeValue(k, traits.LIMIT, traits.qbpp));
++ ErrVal = UnMapErrVal(DecodeValue(k, traits.LIMIT, traits.qbpp));
+ if (ABS(ErrVal) > 65535)
+ throw JlsException(InvalidCompressedData);
+- }
++ }
+ ErrVal = ErrVal ^ ((traits.NEAR == 0) ? ctx.GetErrorCorrection(k) : 0);
+- ctx.UpdateVariables(ErrVal, traits.NEAR, traits.RESET);
++ ctx.UpdateVariables(ErrVal, traits.NEAR, traits.RESET);
+ ErrVal = ApplySign(ErrVal, sign);
+- return traits.ComputeReconstructedSample(Px, ErrVal);
++ return traits.ComputeReconstructedSample(Px, ErrVal);
+ }
+
+
+@@ -245,7 +245,7 @@ public:
+ LONG sign = BitWiseSign(Qs);
+ JlsContext& ctx = _contexts[ApplySign(Qs, sign)];
+ LONG k = ctx.GetGolomb();
+- LONG Px = traits.CorrectPrediction(pred + ApplySign(ctx.C, sign));
++ LONG Px = traits.CorrectPrediction(pred + ApplySign(ctx.C, sign));
+
+ LONG ErrVal = traits.ComputeErrVal(ApplySign(x - Px, sign));
+
+@@ -270,16 +270,16 @@ public:
+ size_t DecodeScan(void* rawData, const JlsRect& size, BYTE **buf, size_t *buf_size, size_t offset, bool bCompare);
+
+ protected:
+- // codec parameters
++ // codec parameters
+ TRAITS traits;
+ JlsRect _rect;
+ int _width;
+- LONG T1;
++ LONG T1;
+ LONG T2;
+- LONG T3;
++ LONG T3;
+
+ // compression context
+- JlsContext _contexts[365];
++ JlsContext _contexts[365];
+ CContextRunMode _contextRunmode[2];
+ LONG _RUNindex;
+ PIXEL* _previousLine; // previous line ptr
+@@ -309,7 +309,7 @@ CTable InitTable(LONG k)
+ CTable table;
+ short nerr;
+ for (nerr = 0; ; nerr++)
+- {
++ {
+ // Q is not used when k != 0
+ LONG merrval = GetMappedErrVal(nerr);//, k, -1);
+ OFPair<LONG, LONG> paircode = CreateEncodedValue(k, merrval);
+@@ -321,7 +321,7 @@ CTable InitTable(LONG k)
+ }
+
+ for (nerr = -1; ; nerr--)
+- {
++ {
+ // Q is not used when k != 0
+ LONG merrval = GetMappedErrVal(nerr);//, k, -1);
+ OFPair<LONG, LONG> paircode = CreateEncodedValue(k, merrval);
+@@ -364,7 +364,7 @@ inlinehint void JlsCodec<TRAITS,STRATEGY>::EncodeMappedValue(LONG k, LONG mapped
+ if (highbits + 1 > 31)
+ {
+ STRATEGY::AppendToBitStream(0, highbits / 2);
+- highbits = highbits - highbits / 2;
++ highbits = highbits - highbits / 2;
+ }
+ STRATEGY::AppendToBitStream(1, highbits + 1);
+ STRATEGY::AppendToBitStream((mappedError & ((1 << k) - 1)), k);
+@@ -374,11 +374,11 @@ inlinehint void JlsCodec<TRAITS,STRATEGY>::EncodeMappedValue(LONG k, LONG mapped
+ if (limit - traits.qbpp > 31)
+ {
+ STRATEGY::AppendToBitStream(0, 31);
+- STRATEGY::AppendToBitStream(1, limit - traits.qbpp - 31);
++ STRATEGY::AppendToBitStream(1, limit - traits.qbpp - 31);
+ }
+ else
+ {
+- STRATEGY::AppendToBitStream(1, limit - traits.qbpp);
++ STRATEGY::AppendToBitStream(1, limit - traits.qbpp);
+ }
+ STRATEGY::AppendToBitStream((mappedError - 1) & ((1 << traits.qbpp) - 1), traits.qbpp);
+ }
+@@ -389,33 +389,33 @@ inlinehint void JlsCodec<TRAITS,STRATEGY>::EncodeMappedValue(LONG k, LONG mapped
+ template<class TRAITS, class STRATEGY>
+ void JlsCodec<TRAITS,STRATEGY>::InitQuantizationLUT()
+ {
+- // for lossless mode with default parameters, we have precomputed te luts for bitcounts 8,10,12 and 16
++ // for lossless mode with default parameters, we have precomputed te luts for bitcounts 8,10,12 and 16
+ if (traits.NEAR == 0 && traits.MAXVAL == (1 << traits.bpp) - 1)
+ {
+ JlsCustomParameters presets = ComputeDefault(traits.MAXVAL, traits.NEAR);
+ if (presets.T1 == T1 && presets.T2 == T2 && presets.T3 == T3)
+ {
+- if (traits.bpp == 8)
++ if (traits.bpp == 8)
+ {
+- _pquant = &rgquant8Ll[rgquant8Ll.size() / 2 ];
++ _pquant = &rgquant8Ll[rgquant8Ll.size() / 2 ];
+ return;
+ }
+- if (traits.bpp == 10)
++ if (traits.bpp == 10)
+ {
+- _pquant = &rgquant10Ll[rgquant10Ll.size() / 2 ];
++ _pquant = &rgquant10Ll[rgquant10Ll.size() / 2 ];
+ return;
+- }
+- if (traits.bpp == 12)
++ }
++ if (traits.bpp == 12)
+ {
+- _pquant = &rgquant12Ll[rgquant12Ll.size() / 2 ];
++ _pquant = &rgquant12Ll[rgquant12Ll.size() / 2 ];
+ return;
+- }
+- if (traits.bpp == 16)
++ }
++ if (traits.bpp == 16)
+ {
+- _pquant = &rgquant16Ll[rgquant16Ll.size() / 2 ];
++ _pquant = &rgquant16Ll[rgquant16Ll.size() / 2 ];
+ return;
+- }
+- }
++ }
++ }
+ }
+
+ LONG RANGE = 1 << traits.bpp;
+@@ -453,7 +453,7 @@ template<class TRAITS, class STRATEGY>
+ LONG JlsCodec<TRAITS,STRATEGY>::DecodeRIError(CContextRunMode& ctx)
+ {
+ LONG k = ctx.GetGolomb();
+- LONG EMErrval = DecodeValue(k, traits.LIMIT - J[_RUNindex]-1, traits.qbpp);
++ LONG EMErrval = DecodeValue(k, traits.LIMIT - J[_RUNindex]-1, traits.qbpp);
+ LONG Errval = ctx.ComputeErrVal(EMErrval + ctx._nRItype, k);
+ ctx.UpdateVariables(Errval, EMErrval);
+ return Errval;
+@@ -466,7 +466,7 @@ void JlsCodec<TRAITS,STRATEGY>::EncodeRIError(CContextRunMode& ctx, LONG Errval)
+ {
+ LONG k = ctx.GetGolomb();
+ bool map = ctx.ComputeMap(Errval, k);
+- LONG EMErrval = 2 * ABS(Errval) - ctx._nRItype - map;
++ LONG EMErrval = 2 * ABS(Errval) - ctx._nRItype - map;
+
+ ASSERT(Errval == ctx.ComputeErrVal(EMErrval + ctx._nRItype, k));
+ EncodeMappedValue(k, EMErrval, traits.LIMIT-J[_RUNindex]-1);
+@@ -476,7 +476,7 @@ void JlsCodec<TRAITS,STRATEGY>::EncodeRIError(CContextRunMode& ctx, LONG Errval)
+
+ template<class TRAITS, class STRATEGY>
+ Triplet<OFTypename TRAITS::SAMPLE> JlsCodec<TRAITS,STRATEGY>::DecodeRIPixel(Triplet<SAMPLE> Ra, Triplet<SAMPLE> Rb)
+-{
++{
+ LONG Errval1 = DecodeRIError(_contextRunmode[0]);
+ LONG Errval2 = DecodeRIError(_contextRunmode[0]);
+ LONG Errval3 = DecodeRIError(_contextRunmode[0]);
+@@ -513,18 +513,18 @@ Triplet<OFTypename TRAITS::SAMPLE> JlsCodec<TRAITS,STRATEGY>::EncodeRIPixel(Trip
+ template<class TRAITS, class STRATEGY>
+ void JlsCodec<TRAITS,STRATEGY>::EncodeRunPixels(LONG runLength, bool endOfLine)
+ {
+- while (runLength >= LONG(1 << J[_RUNindex]))
++ while (runLength >= LONG(1 << J[_RUNindex]))
+ {
+ STRATEGY::AppendOnesToBitStream(1);
+ runLength = runLength - LONG(1 << J[_RUNindex]);
+ IncrementRunIndex();
+ }
+
+- if (endOfLine)
++ if (endOfLine)
+ {
+- if (runLength != 0)
++ if (runLength != 0)
+ {
+- STRATEGY::AppendOnesToBitStream(1);
++ STRATEGY::AppendOnesToBitStream(1);
+ }
+ }
+ else
+@@ -556,7 +556,7 @@ LONG JlsCodec<TRAITS,STRATEGY>::DecodeRunPixels(PIXEL Ra, PIXEL* startPos, LONG
+
+ if (index != cpixelMac)
+ {
+- // incomplete run
++ // incomplete run
+ index += (J[_RUNindex] > 0) ? STRATEGY::ReadValue(J[_RUNindex]) : 0;
+ }
+
+@@ -566,7 +566,7 @@ LONG JlsCodec<TRAITS,STRATEGY>::DecodeRunPixels(PIXEL Ra, PIXEL* startPos, LONG
+ for (LONG i = 0; i < index; ++i)
+ {
+ startPos[i] = Ra;
+- }
++ }
+
+ return index;
+ }
+@@ -582,7 +582,7 @@ LONG JlsCodec<TRAITS,STRATEGY>::DoRunMode(LONG index, EncoderStrategy*)
+
+ LONG runLength = 0;
+
+- while (traits.IsNear(ptypeCurX[runLength],Ra))
++ while (traits.IsNear(ptypeCurX[runLength],Ra))
+ {
+ ptypeCurX[runLength] = Ra;
+ runLength++;
+@@ -629,14 +629,24 @@ void JlsCodec<TRAITS,STRATEGY>::DoLine(SAMPLE*)
+ LONG index = 0;
+ LONG Rb = _previousLine[index-1];
+ LONG Rd = _previousLine[index];
++ LONG RANGE_UPPER = 1 << traits.bpp;
++ LONG RANGE_LOWER = - RANGE_UPPER;
+
+ while(index < _width)
+- {
++ {
+ LONG Ra = _currentLine[index -1];
+ LONG Rc = Rb;
+ Rb = Rd;
+ Rd = _previousLine[index + 1];
+
++ // make sure that values are not out of range
++ if ( (Rd - Rb < RANGE_LOWER) || (Rd - Rb > RANGE_UPPER)
++ || (Rb - Rc < RANGE_LOWER) || (Rb - Rc > RANGE_UPPER)
++ || (Rc - Ra < RANGE_LOWER) || (Rc - Ra > RANGE_UPPER))
++ {
++ throw JlsException(InvalidCompressedData);
++ }
++
+ LONG Qs = ComputeContextID(QuantizeGratient(Rd - Rb), QuantizeGratient(Rb - Rc), QuantizeGratient(Rc - Ra));
+
+ if (Qs != 0)
+@@ -648,8 +658,8 @@ void JlsCodec<TRAITS,STRATEGY>::DoLine(SAMPLE*)
+ {
+ index += DoRunMode(index, (STRATEGY*)(NULL));
+ Rb = _previousLine[index-1];
+- Rd = _previousLine[index];
+- }
++ Rd = _previousLine[index];
++ }
+ }
+ }
+
+@@ -661,7 +671,7 @@ void JlsCodec<TRAITS,STRATEGY>::DoLine(Triplet<SAMPLE>*)
+ {
+ LONG index = 0;
+ while(index < _width)
+- {
++ {
+ Triplet<SAMPLE> Ra = _currentLine[index -1];
+ Triplet<SAMPLE> Rc = _previousLine[index-1];
+ Triplet<SAMPLE> Rb = _previousLine[index];
+@@ -671,7 +681,7 @@ void JlsCodec<TRAITS,STRATEGY>::DoLine(Triplet<SAMPLE>*)
+ LONG Qs2 = ComputeContextID(QuantizeGratient(Rd.v2 - Rb.v2), QuantizeGratient(Rb.v2 - Rc.v2), QuantizeGratient(Rc.v2 - Ra.v2));
+ LONG Qs3 = ComputeContextID(QuantizeGratient(Rd.v3 - Rb.v3), QuantizeGratient(Rb.v3 - Rc.v3), QuantizeGratient(Rc.v3 - Ra.v3));
+
+-
++
+ if (Qs1 == 0 && Qs2 == 0 && Qs3 == 0)
+ {
+ index += DoRunMode(index, (STRATEGY*)(NULL));
+@@ -684,19 +694,19 @@ void JlsCodec<TRAITS,STRATEGY>::DoLine(Triplet<SAMPLE>*)
+ Rx.v3 = DoRegular(Qs3, _currentLine[index].v3, GetPredictedValue(Ra.v3, Rb.v3, Rc.v3), (STRATEGY*)(NULL));
+ _currentLine[index] = Rx;
+ index++;
+- }
++ }
+ }
+ }
+
+
+-// DoScan: Encodes or decodes a scan.
++// DoScan: Encodes or decodes a scan.
+ // In ILV_SAMPLE mode, multiple components are handled in DoLine
+ // In ILV_LINE mode, a call do DoLine is made for every component
+-// In ILV_NONE mode, DoScan is called for each component
++// In ILV_NONE mode, DoScan is called for each component
+
+ template<class TRAITS, class STRATEGY>
+ void JlsCodec<TRAITS,STRATEGY>::DoScan(BYTE **ptr, size_t *size, size_t offset)
+-{
++{
+ _width = Info().width;
+
+ STRATEGY::Init(ptr, size, offset);
+@@ -706,11 +716,11 @@ void JlsCodec<TRAITS,STRATEGY>::DoScan(BYTE **ptr, size_t *size, size_t offset)
+
+ OFVector<PIXEL> vectmp(2 * components * pixelstride);
+ OFVector<LONG> rgRUNindex(components);
+-
++
+ for (LONG line = 0; line < Info().height; ++line)
+ {
+- _previousLine = &vectmp[1];
+- _currentLine = &vectmp[1 + components * pixelstride];
++ _previousLine = &vectmp[1];
++ _currentLine = &vectmp[1 + components * pixelstride];
+ if ((line & 1) == 1)
+ {
+ PIXEL *tmp = _previousLine;
+@@ -724,17 +734,17 @@ void JlsCodec<TRAITS,STRATEGY>::DoScan(BYTE **ptr, size_t *size, size_t offset)
+ for (int component = 0; component < components; ++component)
+ {
+ _RUNindex = rgRUNindex[component];
+-
++
+ // initialize edge pixels used for prediction
+ _previousLine[_width] = _previousLine[_width - 1];
+ _currentLine[-1] = _previousLine[0];
+ DoLine((PIXEL*) NULL); // dummy arg for overload resolution
+-
++
+ rgRUNindex[component] = _RUNindex;
+ _previousLine += pixelstride;
+ _currentLine += pixelstride;
+ }
+-
++
+ if (_rect.Y <= line && line < _rect.Y + _rect.Height)
+ {
+ STRATEGY::OnLineEnd(_rect.Width, _currentLine + _rect.X - (components * pixelstride), pixelstride);
+@@ -754,7 +764,7 @@ ProcessLine* JlsCodec<TRAITS,STRATEGY>::CreateProcess(void* pvoidOut)
+ return new PostProcesSingleComponent(pvoidOut, Info(), sizeof(typename TRAITS::PIXEL));
+
+ if (Info().colorTransform == 0)
+- return new ProcessTransformed<TransformNone<OFTypename TRAITS::SAMPLE> >(pvoidOut, Info(), TransformNone<SAMPLE>());
++ return new ProcessTransformed<TransformNone<OFTypename TRAITS::SAMPLE> >(pvoidOut, Info(), TransformNone<SAMPLE>());
+
+ if (Info().bitspersample == sizeof(SAMPLE)*8)
+ {
+@@ -765,7 +775,7 @@ ProcessLine* JlsCodec<TRAITS,STRATEGY>::CreateProcess(void* pvoidOut)
+ case COLORXFORM_HP3 : return new ProcessTransformed<TransformHp3<SAMPLE> >(pvoidOut, Info(), TransformHp3<SAMPLE>()); break;
+ default: throw JlsException(UnsupportedColorTransform);
+ }
+- }
++ }
+ else if (Info().bitspersample > 8)
+ {
+ int shift = 16 - Info().bitspersample;
+@@ -796,7 +806,7 @@ size_t JlsCodec<TRAITS,STRATEGY>::EncodeScan(const void* rawData, BYTE **ptr, si
+ }
+
+ DoScan(ptr, size, offset);
+-
++
+ return STRATEGY::GetLength();
+
+ }
+@@ -827,7 +837,7 @@ size_t JlsCodec<TRAITS,STRATEGY>::DecodeScan(void* rawData, const JlsRect& rect,
+ _rect = rect;
+
+ DoScan(ptr, size, offset + readBytes);
+-
++
+ return STRATEGY::GetCurBytePos() - (*ptr + offset);
+ }
+
=====================================
debian/patches/0014-CVE-2025-9732.patch
=====================================
@@ -0,0 +1,398 @@
+commit 7ad81d69b19714936e18ea5fc74edaeb9f021ce7
+Author: Joerg Riesmeier <dicom at jriesmeier.com>
+Date: Fri Aug 15 13:35:40 2025 +0200
+
+ 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 at gmail.com> for the report
+ and the sample file (PoC).
+
+--- dcmtk.orig/dcmimage/include/dcmtk/dcmimage/dicopxt.h
++++ dcmtk/dcmimage/include/dcmtk/dcmimage/dicopxt.h
+@@ -574,7 +574,11 @@
+ {
+ /* 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!
+--- dcmtk.orig/dcmimage/include/dcmtk/dcmimage/diybrpxt.h
++++ dcmtk/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 @@
+ // 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);
+ }
+ }
+ }
+--- dcmtk.orig/dcmimgle/libsrc/dcmimage.cc
++++ dcmtk/dcmimgle/libsrc/dcmimage.cc
+@@ -1,6 +1,6 @@
+ /*
+ *
+- * Copyright (C) 1996-2021, 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 @@
+ *(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;
=====================================
debian/patches/0015-CVE-2025-9732b.patch
=====================================
@@ -0,0 +1,40 @@
+commit 3de96da6cd66b1af7224561c568bc3de50cd1398
+Author: Joerg Riesmeier <dicom at jriesmeier.com>
+Date: Mon Aug 18 17:58:56 2025 +0200
+
+ Fixed issue with commit 7ad81d69b.
+
+ Fixed an issue with recently committed changes that fix a problem with
+ invalid YBR_FULL images
+
+diff --git a/dcmimage/include/dcmtk/dcmimage/diybrpxt.h b/dcmimage/include/dcmtk/dcmimage/diybrpxt.h
+index c5415c149..fdaaafc2d 100644
+--- a/dcmimage/include/dcmtk/dcmimage/diybrpxt.h
++++ b/dcmimage/include/dcmtk/dcmimage/diybrpxt.h
+@@ -92,7 +92,7 @@ class DiYBRPixelTemplate
+ // attribute), but not more than the size of the intermediate buffer
+ const unsigned long count = (this->InputCount < this->Count) ? this->InputCount : this->Count;
+ // make sure that there is sufficient input data (for planar pixel data)
+- if (!this->PlanarConfiguration || (count >= planeSize * 3 /* number of planes */))
++ if (!this->PlanarConfiguration || (count >= planeSize))
+ {
+ if (rgb) /* convert to RGB model */
+ {
+@@ -231,7 +231,7 @@ class DiYBRPixelTemplate
+ const T1 *p = pixel;
+ if (this->PlanarConfiguration)
+ {
+- /*
++/*
+ T2 *q;
+ // number of pixels to be skipped (only applicable if 'PixelData' contains more
+ // pixels than expected)
+@@ -244,7 +244,7 @@ class DiYBRPixelTemplate
+ // skip to beginning of next plane
+ p += skip;
+ }
+- */
++*/
+ unsigned long l;
+ unsigned long i = 0;
+ while (i < count)
=====================================
debian/patches/0016-CVE-2025-14607.patch
=====================================
@@ -0,0 +1,31 @@
+commit 4c0e5c10079392c594d6a7abd95dd78ac0aa556a
+Author: Marco Eichelberg <eichelberg at offis.de>
+Date: Tue Dec 2 09:06:30 2025 +0100
+
+ Fixed bug in handling of odd-length data elements.
+
+ When a dataset containing an illegal odd-length attribute with a text VR
+ was read from file or received over a network connection, then accessing
+ the value of that attribute with DcmElement::getString() may return a
+ pointer to a string that was not properly null terminated. Using C string
+ functions such as strlen() or strcpy() on that string then lead to a read
+ beyond the end of a string, causing a segmentation fault.
+
+ Thanks to Zou Dikai <zoudikai at outlook.com> for the bug report and POC.
+
+ This closes DCMTK issue #1184.
+
+--- dcmtk.orig/dcmdata/libsrc/dcbytstr.cc
++++ dcmtk/dcmdata/libsrc/dcbytstr.cc
+@@ -658,7 +658,11 @@
+
+ /* terminate string after real length */
+ if (value != NULL)
++ {
+ value[lengthField] = 0;
++ value[lengthField+1] = 0;
++ }
++
+ /* enforce old (pre DCMTK 3.5.2) behaviour? */
+ if (!dcmAcceptOddAttributeLength.get())
+ {
=====================================
debian/patches/0017-CVE-2025-14841.patch
=====================================
@@ -0,0 +1,37 @@
+commit ffb1a4a37d2c876e3feeb31df4930f2aed7fa030
+Author: Marco Eichelberg <eichelberg at offis.de>
+Date: Fri Nov 28 12:24:07 2025 +0100
+
+ Fixed two possible segfaults in dcmqrscp.
+
+ Fixed two places where invalid messages may trigger a segmentation fault
+ due to a NULL pointer being de-referenced.
+
+ Thanks to 邹 迪凯 <zoudikai at outlook.com> for the bug report and proof-of-concept.
+
+--- dcmtk.orig/dcmqrdb/libsrc/dcmqrdbi.cc
++++ dcmtk/dcmqrdb/libsrc/dcmqrdbi.cc
+@@ -1381,8 +1381,10 @@
+ /* only char string type tags are supported at the moment */
+ char *s = NULL;
+ dcelem->getString(s);
++
+ /* the available space is always elem.ValueLength+1 */
+- OFStandard::strlcpy(elem.PValueField, s, elem.ValueLength+1);
++ if (s) OFStandard::strlcpy(elem.PValueField, s, elem.ValueLength+1);
++ else elem.PValueField[0]='\0';
+ }
+ /** If element is the Query Level, store it in handle
+ */
+@@ -2066,8 +2068,10 @@
+ /* only char string type tags are supported at the moment */
+ char *s = NULL;
+ dcelem->getString(s);
++
+ /* the available space is always elem.ValueLength+1 */
+- OFStandard::strlcpy(elem.PValueField, s, elem.ValueLength+1);
++ if (s) OFStandard::strlcpy(elem.PValueField, s, elem.ValueLength+1);
++ else elem.PValueField[0]='\0';
+ }
+
+ /** If element is the Query Level, store it in handle
=====================================
debian/patches/0018-CVE-2026-5663.patch
=====================================
@@ -0,0 +1,274 @@
+commit edbb085e45788dccaf0e64d71534cfca925784b8
+Author: Marco Eichelberg <eichelberg at offis.de>
+Date: Sat Mar 21 18:35:14 2026 +0100
+
+ Sanitize all strings passed to the exec options.
+
+ Sanitize the text fields from incoming DICOM associations and DICOM objects
+ (such as Study Instance UID, SOP Instance UID, Patient's Name) and the
+ calling SCU's network presentation address by removing special characters
+ that may be interpreted as shell escape characters when one of the
+ execution options (e.g. --exec-on-reception) is in use.
+
+ Thanks to Machine Spirits UG (haftungsbeschränkt) for the bug report,
+ detailed analysis and proof of concept.
+
+ This closes DCMTK issue #1194.
+
+--- dcmtk.orig/dcmnet/apps/storescp.cc
++++ dcmtk/dcmnet/apps/storescp.cc
+@@ -1,6 +1,6 @@
+ /*
+ *
+- * Copyright (C) 1994-2021, OFFIS e.V.
++ * Copyright (C) 1994-2026, OFFIS e.V.
+ * All rights reserved. See COPYRIGHT file for details.
+ *
+ * This software and supporting documentation were developed by
+@@ -201,6 +201,41 @@
+ #define SHORTCOL 4
+ #define LONGCOL 21
+
++// Allow list used by sanitizeAETitle(). Index is (byte - 32), so the
++// table covers the printable ASCII range 0x20..0x7E. Every entry either
++// repeats the input byte (kept) or is '_' (replaced). Kept characters:
++// space, '-', '.', ':', '@', '_' and ASCII letters/digits. All shell
++// metacharacters and path separators map to '_'.
++static const char sanitized_aetitle_charset[] =
++{
++ ' ', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '-', '.', '_',
++ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', '_', '_', '_', '_', '_',
++ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
++ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '_', '_', '_', '_', '_',
++ '_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
++ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '_', '_', '_', '_', '_'
++};
++
++
++inline void sanitizeAETitle(OFString& aetitle)
++{
++ // Preserve a surrounding pair of quotation marks (used by callers
++ // that substitute the AE title into an already quoted shell argument).
++ size_t len = aetitle.length();
++ size_t start = 0;
++ if (len >= 2 && aetitle[0] == '"' && aetitle[len - 1] == '"')
++ {
++ start = 1;
++ --len;
++ }
++ for (size_t i = start; i < len; ++i)
++ {
++ unsigned char c = OFstatic_cast(unsigned char, aetitle[i]);
++ if (c < 32 || c >= 127) aetitle[i] = '_';
++ else aetitle[i] = sanitized_aetitle_charset[c - 32];
++ }
++}
++
+ int main(int argc, char *argv[])
+ {
+ T_ASC_Network *net;
+@@ -1474,7 +1509,9 @@
+ calledAETitle.clear();
+ }
+ // store calling presentation address (i.e. remote hostname)
+- callingPresentationAddress = OFSTRING_GUARD(assoc->params->DULparams.callingPresentationAddress);
++ callingPresentationAddress = "\"";
++ callingPresentationAddress += OFSTRING_GUARD(assoc->params->DULparams.callingPresentationAddress);
++ callingPresentationAddress += "\"";
+
+ /* now do the real work, i.e. receive DIMSE commands over the network connection */
+ /* which was established and handle these commands correspondingly. In case of */
+@@ -1838,6 +1875,7 @@
+ dateTime.getTime().getHour(), dateTime.getTime().getMinute(), dateTime.getTime().getIntSecond(), dateTime.getTime().getMilliSecond());
+
+ OFString subdirectoryName;
++ OFString s;
+ switch (opt_sortStudyMode)
+ {
+ case ESM_Timestamp:
+@@ -1852,15 +1890,27 @@
+ subdirectoryName = opt_sortStudyDirPrefix;
+ if (!subdirectoryName.empty())
+ subdirectoryName += '_';
+- subdirectoryName += currentStudyInstanceUID;
+- OFStandard::sanitizeFilename(subdirectoryName);
++ s = currentStudyInstanceUID;
++ OFStandard::sanitizeFilename(s);
++ if (s != currentStudyInstanceUID)
++ {
++ OFLOG_WARN(storescpLogger, "Sanitized unusual characters in Study Instance UID, converted from \"" << currentStudyInstanceUID << "\" to \"" << s << "\".");
++ }
++ subdirectoryName += s;
+ break;
+ case ESM_PatientName:
+ // pattern: "[Patient's Name]_[YYYYMMDD]_[HHMMSSMMM]"
+ subdirectoryName = currentPatientName;
++ OFStandard::sanitizeFilename(subdirectoryName);
++ if (subdirectoryName != currentPatientName)
++ {
++ // It is quite normal that we need to sanitize characters in PatientName.
++ // Therefore, this is only a debug message and not a warning, unlike the other
++ // messages about sanitized fields, which are normally not expected.
++ OFLOG_DEBUG(storescpLogger, "Sanitized characters in Patient Name, converted from \"" << currentPatientName << "\" to \"" << subdirectoryName << "\".");
++ }
+ subdirectoryName += '_';
+ subdirectoryName += timestamp;
+- OFStandard::sanitizeFilename(subdirectoryName);
+ break;
+ case ESM_None:
+ break;
+@@ -2068,8 +2118,13 @@
+ else
+ {
+ // Use the SOP instance UID as found in the C-STORE request message as part of the filename
+- OFString uid = req->AffectedSOPInstanceUID;
++ OFString s(OFSTRING_GUARD(req->AffectedSOPInstanceUID));
++ OFString uid = s;
+ OFStandard::sanitizeFilename(uid);
++ if (uid != s)
++ {
++ OFLOG_WARN(storescpLogger, "Sanitized unusual characters in SOP Instance UID, converted from \"" << s << "\" to \"" << uid << "\".");
++ }
+ sprintf(imageFileName, "%s%c%s.%s%s", opt_outputDirectory.c_str(), PATH_SEPARATOR, dcmSOPClassUIDToModality(req->AffectedSOPClassUID, "UNKNOWN"),
+ uid.c_str(), opt_fileNameExtension.c_str());
+ }
+@@ -2202,28 +2257,40 @@
+ */
+ {
+ OFString cmd = opt_execOnReception;
++ OFString s;
+
+ // in case a file was actually written
+ if( !opt_ignore )
+ {
+ // perform substitution for placeholder #p (depending on presence of any --sort-xxx option)
++ // Note: We do not enclose this in quotes because it may be used as part of a path expression.
+ OFString dir = (opt_sortStudyMode == ESM_None) ? opt_outputDirectory : subdirectoryPathAndName;
+ cmd = replaceChars( cmd, OFString(PATH_PLACEHOLDER), dir );
+
+ // perform substitution for placeholder #f; note that outputFileNameArray.back()
+ // always contains the name of the file (without path) which was written last.
++ // Note: We do not enclose this in quotes because it may be used as part of a path expression.
+ OFString outputFileName = outputFileNameArray.back();
+ cmd = replaceChars( cmd, OFString(FILENAME_PLACEHOLDER), outputFileName );
+ }
+
+- // perform substitution for placeholder #a
++ // perform substitution for placeholder #a.
++ // Note that this string is already enclosed in double quotes at this point
+ cmd = replaceChars( cmd, OFString(CALLING_AETITLE_PLACEHOLDER), callingAETitle );
+
+- // perform substitution for placeholder #c
++ // perform substitution for placeholder #c.
++ // Note that this string is already enclosed in double quotes at this point
+ cmd = replaceChars( cmd, OFString(CALLED_AETITLE_PLACEHOLDER), calledAETitle );
+
+- // perform substitution for placeholder #r
+- cmd = replaceChars( cmd, OFString(CALLING_PRESENTATION_ADDRESS_PLACEHOLDER), callingPresentationAddress );
++ // perform substitution for placeholder #r.
++ // Note that this string is already enclosed in double quotes at this point
++ s = callingPresentationAddress;
++ sanitizeAETitle(s);
++ if (s != callingPresentationAddress)
++ {
++ OFLOG_WARN(storescpLogger, "Sanitized unusual characters in calling presentation address, converted from " << callingPresentationAddress << " to " << s << ".");
++ }
++ cmd = replaceChars( cmd, OFString(CALLING_PRESENTATION_ADDRESS_PLACEHOLDER), s );
+
+ // Execute command in a new process
+ executeCommand( cmd );
+@@ -2325,18 +2392,41 @@
+ */
+ {
+ OFString cmd = opt_execOnEndOfStudy;
++ OFString s;
+
+ // perform substitution for placeholder #p; #p will be substituted by lastStudySubdirectoryPathAndName
++ // Note: We do not enclose this in quotes because it may be used as part of a path expression.
+ cmd = replaceChars( cmd, OFString(PATH_PLACEHOLDER), lastStudySubdirectoryPathAndName );
+
+- // perform substitution for placeholder #a
+- cmd = replaceChars( cmd, OFString(CALLING_AETITLE_PLACEHOLDER), callingAETitle );
++ // perform substitution for placeholder #a.
++ // Note that this string is already enclosed in double quotes at this point
++ s = callingAETitle;
++ sanitizeAETitle(s);
++ if (s != callingAETitle)
++ {
++ OFLOG_WARN(storescpLogger, "Sanitized unusual characters in calling aetitle, converted from " << callingAETitle << " to " << s << ".");
++ }
++ cmd = replaceChars( cmd, OFString(CALLING_AETITLE_PLACEHOLDER), s );
+
+- // perform substitution for placeholder #c
+- cmd = replaceChars( cmd, OFString(CALLED_AETITLE_PLACEHOLDER), calledAETitle );
++ // perform substitution for placeholder #c.
++ // Note that this string is already enclosed in double quotes at this point
++ s = calledAETitle;
++ sanitizeAETitle(s);
++ if (s != calledAETitle)
++ {
++ OFLOG_WARN(storescpLogger, "Sanitized unusual characters in called aetitle, converted from " << calledAETitle << " to " << s << ".");
++ }
++ cmd = replaceChars( cmd, OFString(CALLED_AETITLE_PLACEHOLDER), s );
+
+- // perform substitution for placeholder #r
+- cmd = replaceChars( cmd, OFString(CALLING_PRESENTATION_ADDRESS_PLACEHOLDER), callingPresentationAddress );
++ // perform substitution for placeholder #r.
++ // Note that this string is already enclosed in double quotes at this point
++ s = callingPresentationAddress;
++ sanitizeAETitle(s);
++ if (s != callingPresentationAddress)
++ {
++ OFLOG_WARN(storescpLogger, "Sanitized unusual characters in calling presentation address, converted from " << callingPresentationAddress << " to " << s << ".");
++ }
++ cmd = replaceChars( cmd, OFString(CALLING_PRESENTATION_ADDRESS_PLACEHOLDER), s );
+
+ // Execute command in a new process
+ executeCommand( cmd );
+--- dcmtk.orig/ofstd/libsrc/ofstd.cc
++++ dcmtk/ofstd/libsrc/ofstd.cc
+@@ -3246,16 +3246,26 @@
+ }
+
+
++static const char sanitized_filename_charset[] =
++{
++ ' ', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '-', '.', '_',
++ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', '_', '_', '_', '_', '_',
++ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
++ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '_', '_', '_', '_', '_',
++ '_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
++ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '_', '_', '_', '_', '_'
++};
++
++
+ void OFStandard::sanitizeFilename(OFString& fname)
+ {
+ size_t len = fname.length();
++ char c;
+ for (size_t i=0; i<len; ++i)
+ {
+-#ifdef _WIN32
+- if ((fname[i] == PATH_SEPARATOR)||(fname[i] == '/')) fname[i] = '_';
+-#else
+- if (fname[i] == PATH_SEPARATOR) fname[i] = '_';
+-#endif
++ c = fname[i];
++ if (c != 0 && (c < 32 || c >= 127)) c = '_'; else c = sanitized_filename_charset[c-32];
++ fname[i] = c;
+ }
+ }
+
+@@ -3267,11 +3277,7 @@
+ char *c = fname;
+ while (*c)
+ {
+-#ifdef _WIN32
+- if ((*c == PATH_SEPARATOR)||(*c == '/')) *c = '_';
+-#else
+- if (*c == PATH_SEPARATOR) *c = '_';
+-#endif
++ if (*c < 32 || *c >= 127) *c = '_'; else *c = sanitized_filename_charset[*c-32];
+ ++c;
+ }
+ }
=====================================
debian/patches/0019-CVE-2026-10194.patch
=====================================
@@ -0,0 +1,66 @@
+commit 0f78a4ef6f645ea5530166e445e5436a5de58e75
+Author: Marco Eichelberg <eichelberg at offis.de>
+Date: Mon May 4 17:48:30 2026 +0200
+
+ Fixed remote heap buffer overflow in dcmqrscp.
+
+ Thanks to 'elp3pinill0' for the bug report, detailed
+ analysis, proof of concept and proposed fix.
+
+ This closes DCMTK issue #1206.
+
+--- dcmtk.orig/dcmqrdb/libsrc/dcmqrdbi.cc
++++ dcmtk/dcmqrdb/libsrc/dcmqrdbi.cc
+@@ -1,6 +1,6 @@
+ /*
+ *
+- * Copyright (C) 1993-2022, OFFIS e.V.
++ * Copyright (C) 1993-2026, OFFIS e.V.
+ * All rights reserved. See COPYRIGHT file for details.
+ *
+ * This software and supporting documentation were developed by
+@@ -2475,12 +2475,16 @@
+
+ DB_IdxInitLoop (&(handle_ -> idxCounter)) ;
+ while ( DB_IdxGetNext(&(handle_ -> idxCounter), &idxRec) == EC_Normal ) {
+- if ( ! ( strncmp(idxRec. StudyInstanceUID, StudyUID, n) ) ) {
+-
+- StudyArray[nbimages]. idxCounter = handle_ -> idxCounter ;
+- StudyArray[nbimages]. RecordedDate = idxRec. RecordedDate ;
+- StudyArray[nbimages++]. ImageSize = idxRec. ImageSize ;
+- }
++ if ( ! ( strncmp(idxRec. StudyInstanceUID, StudyUID, n) ) ) {
++ StudyArray[nbimages]. idxCounter = handle_ -> idxCounter ;
++ StudyArray[nbimages]. RecordedDate = idxRec. RecordedDate ;
++ StudyArray[nbimages++]. ImageSize = idxRec. ImageSize ;
++ if (nbimages == MAX_NUMBER_OF_IMAGES) {
++ // too many images in this study, bail out
++ DCMQRDB_ERROR("maximum number of images per study (" << MAX_NUMBER_OF_IMAGES << ") exceeded");
++ return QR_EC_IndexDatabaseError;
++ }
++ }
+ }
+
+ /** Sort the StudyArray in order to have the oldest images first
+@@ -2567,6 +2571,8 @@
+ s = matchStudyUIDInStudyDesc (pStudyDesc, StudyUID,
+ (int)(handle_ -> maxStudiesAllowed)) ;
+
++ OFCondition cond;
++
+ /** If Study already exists
+ */
+
+@@ -2587,10 +2593,10 @@
+
+ RequiredSize = imageSize -
+ ( handle_ -> maxBytesPerStudy - pStudyDesc[s]. StudySize ) ;
+- deleteOldestImages(pStudyDesc, s, StudyUID, RequiredSize) ;
++ cond = deleteOldestImages(pStudyDesc, s, StudyUID, RequiredSize) ;
++ if (cond.bad()) return cond;
+ }
+
+-
+ }
+ else {
+ #ifdef DEBUG
=====================================
debian/patches/series
=====================================
@@ -21,3 +21,11 @@ c34f4e46e672ad21accf04da0dc085e43be6f5e1.patch
0009-CVE-2025-25475.patch
0010-CVE-2025-25474.patch
0011-CVE-2025-25472.patch
+0012-CVE-2022-4981.patch
+0013-CVE-2025-2357.patch
+0014-CVE-2025-9732.patch
+0015-CVE-2025-9732b.patch
+0016-CVE-2025-14607.patch
+0017-CVE-2025-14841.patch
+0018-CVE-2026-5663.patch
+0019-CVE-2026-10194.patch
View it on GitLab: https://salsa.debian.org/med-team/dcmtk/-/compare/895d58dedeed1d92ea5221ee6129e9479d6efb18...b247b97c9820fb692e068653719750f5cdc46f35
--
View it on GitLab: https://salsa.debian.org/med-team/dcmtk/-/compare/895d58dedeed1d92ea5221ee6129e9479d6efb18...b247b97c9820fb692e068653719750f5cdc46f35
You're receiving this email because of your account on salsa.debian.org. Manage all notifications: https://salsa.debian.org/-/profile/notifications | Help: https://salsa.debian.org/help
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20260613/7edd4789/attachment-0001.htm>
More information about the debian-med-commit
mailing list