From: Michael Onken Date: Mon, 6 Jul 2026 20:39:03 +0000 (+0200) Subject: Fix wlmscpfs crash on VR-spoofed seq. attributes. X-Git-Tag: archive/raspbian/3.7.0+really3.7.0-7+rpi1^2~2 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=d6244c102a6ef4ba0bb4d294420db6f21e673945;p=dcmtk.git Fix wlmscpfs crash on VR-spoofed seq. attributes. Applied-Upstream: f4e0074682645b1a4289d62581926c4394d5c6d5 Last-Update: 2026-05-29 Reviewed-By: Étienne Mollier Bug-Debian: https://bugs.debian.org/1141411 wldsfs.cc cast findAndGetElement() results to DcmSequenceOfItems* without checking the VR. A C-FIND with a dictionary-SQ tag declared under a non-SQ wire VR (Explicit VR) thus dispatched through the wrong vtable and crashes the SCP (see also CVE-2024-28130 in dcmpstat). Use findAndGetSequence(), which validates the VR. Also guard getItem(0) against an empty ScheduledProcedureStepSequence and drop a bogus cast. Thanks for the report and analysis to Abhinav Agarwal. This closes DCMTK issue #1218. Gbp-Pq: Name CVE-2026-44628a.patch --- diff --git a/dcmwlm/libsrc/wldsfs.cc b/dcmwlm/libsrc/wldsfs.cc index ee83d7b0..7bb4db1f 100644 --- a/dcmwlm/libsrc/wldsfs.cc +++ b/dcmwlm/libsrc/wldsfs.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1996-2025, OFFIS e.V. + * Copyright (C) 1996-2026, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -171,20 +171,23 @@ void WlmDataSourceFileSystem::HandleExistentButEmptyDescriptionAndCodeSequenceAt // codeSequenceTagKey - [in] DcmTagKey of the codeSequence attribute which shall be checked. // Return Value : none. { - DcmElement *codeSequenceAttribute = NULL, *descriptionAttribute = NULL; + DcmSequenceOfItems *codeSequenceAttribute = NULL; + DcmElement *descriptionAttribute = NULL; DcmElement *elementToRemove = NULL, *codeValueAttribute = NULL, *codingSchemeDesignatorAttribute = NULL; OFBool codeSequenceAttributeRemoved = OFFalse; - // only do something with the code sequence attribute if it is contained in the dataset - if( dataset->findAndGetElement( codeSequenceTagKey, codeSequenceAttribute ).good() ) + // only do something with the code sequence attribute if it is contained in the dataset. + // Note: findAndGetSequence() verifies the element's VR (SQ); a malformed element with a + // dictionary-SQ tag but a non-SQ wire VR is rejected here instead of being cast blindly. + if( dataset->findAndGetSequence( codeSequenceTagKey, codeSequenceAttribute ).good() ) { // if the code sequence attribute is empty or contains exactly one item with an empty // CodeValue and an empty CodingSchemeDesignator, remove the attribute from the dataset - if( ( ((DcmSequenceOfItems*)codeSequenceAttribute)->card() == 0 ) || - ( ((DcmSequenceOfItems*)codeSequenceAttribute)->card() == 1 && - ((DcmSequenceOfItems*)codeSequenceAttribute)->getItem(0)->findAndGetElement( DCM_CodeValue, codeValueAttribute ).good() && + if( ( codeSequenceAttribute->card() == 0 ) || + ( codeSequenceAttribute->card() == 1 && + codeSequenceAttribute->getItem(0)->findAndGetElement( DCM_CodeValue, codeValueAttribute ).good() && codeValueAttribute->getLength() == 0 && - ((DcmSequenceOfItems*)codeSequenceAttribute)->getItem(0)->findAndGetElement( DCM_CodingSchemeDesignator, codingSchemeDesignatorAttribute ).good() && + codeSequenceAttribute->getItem(0)->findAndGetElement( DCM_CodingSchemeDesignator, codingSchemeDesignatorAttribute ).good() && codingSchemeDesignatorAttribute->getLength() == 0 ) ) { elementToRemove = dataset->remove( codeSequenceAttribute ); @@ -221,18 +224,21 @@ void WlmDataSourceFileSystem::HandleExistentButEmptyReferencedStudyOrPatientSequ // sequenceTagKey - [in] DcmTagKey of the sequence attribute which shall be checked. // Return Value : none. { - DcmElement *sequenceAttribute = NULL, *referencedSOPClassUIDAttribute = NULL, *referencedSOPInstanceUIDAttribute = NULL; + DcmSequenceOfItems *sequenceAttribute = NULL; + DcmElement *referencedSOPClassUIDAttribute = NULL, *referencedSOPInstanceUIDAttribute = NULL; // in case the sequence attribute contains exactly one item with an empty - // ReferencedSOPClassUID and an empty ReferencedSOPInstanceUID, remove the item - if( dataset->findAndGetElement( sequenceTagKey, sequenceAttribute ).good() && - ( (DcmSequenceOfItems*)sequenceAttribute )->card() == 1 && - ( (DcmSequenceOfItems*)sequenceAttribute )->getItem(0)->findAndGetElement( DCM_ReferencedSOPClassUID, referencedSOPClassUIDAttribute ).good() && + // ReferencedSOPClassUID and an empty ReferencedSOPInstanceUID, remove the item. + // Note: findAndGetSequence() verifies the element's VR (SQ); a malformed element with a + // dictionary-SQ tag but a non-SQ wire VR is rejected here instead of being cast blindly. + if( dataset->findAndGetSequence( sequenceTagKey, sequenceAttribute ).good() && + sequenceAttribute->card() == 1 && + sequenceAttribute->getItem(0)->findAndGetElement( DCM_ReferencedSOPClassUID, referencedSOPClassUIDAttribute ).good() && referencedSOPClassUIDAttribute->getLength() == 0 && - ( (DcmSequenceOfItems*)sequenceAttribute )->getItem(0)->findAndGetElement( DCM_ReferencedSOPInstanceUID, referencedSOPInstanceUIDAttribute, OFFalse ).good() && + sequenceAttribute->getItem(0)->findAndGetElement( DCM_ReferencedSOPInstanceUID, referencedSOPInstanceUIDAttribute, OFFalse ).good() && referencedSOPInstanceUIDAttribute->getLength() == 0 ) { - DcmItem *item = ((DcmSequenceOfItems*)sequenceAttribute)->remove( ((DcmSequenceOfItems*)sequenceAttribute)->getItem(0) ); + DcmItem *item = sequenceAttribute->remove( sequenceAttribute->getItem(0) ); delete item; } } @@ -257,7 +263,7 @@ WlmDataSourceStatusType WlmDataSourceFileSystem::StartFindRequest( const DcmData // WLM_FAILED_IDENTIFIER_DOES_NOT_MATCH_SOP_CLASS - Error in the search mask encountered. { unsigned long i, j; - DcmElement *scheduledProcedureStepSequenceAttribute = NULL; + DcmSequenceOfItems *scheduledProcedureStepSequenceAttribute = NULL; // Initialize offending elements, error elements and error comment. delete offendingElements; @@ -352,9 +358,13 @@ WlmDataSourceStatusType WlmDataSourceFileSystem::StartFindRequest( const DcmData } // if the ScheduledProcedureStepSequence can be found in the current dataset, handle - // existent but empty ScheduledProcedureStepDescription and ScheduledProtocolCodeSequence - if( resultRecord->findAndGetElement( DCM_ScheduledProcedureStepSequence, scheduledProcedureStepSequenceAttribute, OFFalse ).good() ) - HandleExistentButEmptyDescriptionAndCodeSequenceAttributes( ((DcmDataset*)((DcmSequenceOfItems*)scheduledProcedureStepSequenceAttribute)->getItem(0)), DCM_ScheduledProcedureStepDescription, DCM_ScheduledProtocolCodeSequence ); + // existent but empty ScheduledProcedureStepDescription and ScheduledProtocolCodeSequence. + // Note: findAndGetSequence() verifies the element's VR (SQ); a malformed element with a + // dictionary-SQ tag but a non-SQ wire VR is rejected here instead of being cast blindly. + // Also guard against an empty sequence so that getItem(0) does not return NULL. + if( resultRecord->findAndGetSequence( DCM_ScheduledProcedureStepSequence, scheduledProcedureStepSequenceAttribute, OFFalse ).good() && + scheduledProcedureStepSequenceAttribute->card() > 0 ) + HandleExistentButEmptyDescriptionAndCodeSequenceAttributes( scheduledProcedureStepSequenceAttribute->getItem(0), DCM_ScheduledProcedureStepDescription, DCM_ScheduledProtocolCodeSequence ); // handle existent but empty RequestedProcedureDescription and RequestedProcedureCodeSequence HandleExistentButEmptyDescriptionAndCodeSequenceAttributes( resultRecord, DCM_RequestedProcedureDescription, DCM_RequestedProcedureCodeSequence );