CVE-2021-41688 (C++, published 2022-06-28T13:15Z)
Description: value: DCMTK through 3.6.6 does not handle memory free properly. The object in the program is free but its address is still used in other locations. Sending specific requests to the dcmqrdb program will incur a double free. An attacker can use it to launch a DoS attack.
Severity: MEDIUM (CVSS v3 base score: 7.5)
Weakness: CWE-415 \u2014 Double Free
The product calls free() twice on the same memory address, potentially leading to modification of unexpected memory locations.
Repository: https://github.com/DCMTK/dcmtk
Files changed:
dcmnet/include/dcmtk/dcmnet/dulstruc.h
dcmnet/include/dcmtk/dcmnet/helpers.h
dcmnet/libsrc/CMakeLists.txt
dcmnet/libsrc/Makefile.in
dcmnet/libsrc/assoc.cc
dcmnet/libsrc/dcuserid.cc
dcmnet/libsrc/dul.cc
dcmnet/libsrc/dulconst.cc
dcmnet/libsrc/dulextra.cc
dcmnet/libsrc/dulfsm.cc
dcmnet/libsrc/dulparse.cc
dcmnet/libsrc/dulpres.cc
dcmnet/libsrc/helpers.cc
Commit message: Fixed poss. NULL pointer dereference/double free.

Thanks to Jinsheng Ba <bajinsheng@u.nus.edu> for the report and some patches.


Vulnerable code:
	dcuserid.o scu.o scp.o scpcfg.o scpthrd.o scppool.o dwrap.o
static void
destroyPresentationContextList(LST_HEAD ** lst)
{
    DUL_PRESENTATIONCONTEXT *pc;
    DUL_TRANSFERSYNTAX *ts;

    if ((lst == NULL) || (*lst == NULL))
        return;
    while ((pc = (DUL_PRESENTATIONCONTEXT*) LST_Dequeue(lst)) != NULL) {
        if (pc->proposedTransferSyntax != NULL) {
            while ((ts = (DUL_TRANSFERSYNTAX*) LST_Dequeue(&pc->proposedTransferSyntax)) != NULL) {
                free(ts);
            }
            LST_Destroy(&pc->proposedTransferSyntax);
        }
        free(pc);
    }
    LST_Destroy(lst);
}
        cond = ASC_destroyAssociationParameters(&(*association)->params);
        if (cond.bad()) return cond;
 *  Copyright (C) 1997-2018, OFFIS e.V.
#include "dulstruc.h"
#include "dulstruc.h"
#include "dulstruc.h"
#include "dulstruc.h"
#include "dulstruc.h"
void destroyPresentationContextList(LST_HEAD ** l);
void destroyUserInformationLists(DUL_USERINFO * userInfo);


void
destroyPresentationContextList(LST_HEAD ** l)
{
    PRV_PRESENTATIONCONTEXTITEM
    * prvCtx;
    DUL_SUBITEM
        * subItem;

    if (*l == NULL)
        return;

    prvCtx = (PRV_PRESENTATIONCONTEXTITEM*)LST_Dequeue(l);
    while (prvCtx != NULL) {
        subItem = (DUL_SUBITEM*)LST_Dequeue(&prvCtx->transferSyntaxList);
        while (subItem != NULL) {
            free(subItem);
            subItem = (DUL_SUBITEM*)LST_Dequeue(&prvCtx->transferSyntaxList);
        }
        LST_Destroy(&prvCtx->transferSyntaxList);
        free(prvCtx);
        prvCtx = (PRV_PRESENTATIONCONTEXTITEM*)LST_Dequeue(l);
    }
    LST_Destroy(l);
}

void
destroyUserInformationLists(DUL_USERINFO * userInfo)
{
    PRV_SCUSCPROLE
    * role;

    role = (PRV_SCUSCPROLE*)LST_Dequeue(&userInfo->SCUSCPRoleList);
    while (role != NULL) {
        free(role);
        role = (PRV_SCUSCPROLE*)LST_Dequeue(&userInfo->SCUSCPRoleList);
    }
    LST_Destroy(&userInfo->SCUSCPRoleList);

    /* extended negotiation */
    delete userInfo->extNegList; userInfo->extNegList = NULL;

    /* user identity negotiation */
    delete userInfo->usrIdent; userInfo->usrIdent = NULL;
}
#include "dulstruc.h"
    if ((assoc->presentationContextList = LST_Create()) == NULL) return EC_MemoryExhausted;
    if ((assoc->userInfo.SCUSCPRoleList = LST_Create()) == NULL) return EC_MemoryExhausted;

                  return makeUnderflowError("Application Context item", pduLength, itemLength);
                DCMNET_TRACE("Successfully parsed Application Context");
            if (context == NULL) return EC_MemoryExhausted;
            (void) memset(context, 0, sizeof(*context));
            cond = parsePresentationContext(type, context, buf, &itemLength, pduLength);
            if (cond.bad()) return cond;
            buf += itemLength;
            if (!OFStandard::safeSubtract(pduLength, itemLength, pduLength))
              return makeUnderflowError("Presentation Context item", pduLength, itemLength);
            LST_Enqueue(&assoc->presentationContextList, (LST_NODE*)context);
            DCMNET_TRACE("Successfully parsed Presentation Context");
            if (cond.bad())
                return cond;
            buf += itemLength;
            if (!OFStandard::safeSubtract(pduLength, itemLength, pduLength))
              return makeUnderflowError("User Information item", pduLength, itemLength);
            DCMNET_TRACE("Successfully parsed User Information");
            if (cond.bad())
                return cond;
            buf += itemLength;
            if (!OFStandard::safeSubtract(pduLength, itemLength, pduLength))
              return makeUnderflowError("unknown item type", pduLength, itemLength);
                if (cond.bad()) return cond;
            if (cond.bad()) return cond;
#include "dulstruc.h"

Fixed code:
#include "dcmtk/dcmnet/dicom.h"
#include "dcmtk/dcmnet/dntypes.h"
#include "dcmtk/dcmnet/dul.h"
#include "dcmtk/dcmnet/lst.h"




/*
 *
 *  Copyright (C) 2021, OFFIS e.V.
 *  All rights reserved.  See COPYRIGHT file for details.
 *
 *  This software and supporting documentation were partly developed by
 *
 *    OFFIS e.V.
 *    R&D Division Health
 *    Escherweg 2
 *    D-26121 Oldenburg, Germany
 *
 *
 *  Module: dcmnet
 *
 *  Author: Michael Onken
 *
 *  Purpose: Collection of helper functions
 *
 */

#ifndef DCMHET_HELPERS_H
#define DCMHET_HELPERS_H

#include "dcmtk/ofstd/ofcond.h"
#include "dcmtk/dcmnet/dulstruc.h"

struct T_ASC_Parameters;
class LST_HEAD;


void
destroyPresentationContextList(LST_HEAD ** l);

void
destroyUserInformationLists(DUL_USERINFO * userInfo);

#endif
  helpers.cc
	dcuserid.o helper.o scu.o scp.o scpcfg.o scpthrd.o scppool.o dwrap.o
#include "dcmtk/dcmnet/helpers.h"
        ASC_destroyAssociationParameters(&(*association)->params);
 *  Copyright (C) 1997-2021, OFFIS e.V.
#include "dcmtk/dcmnet/dulstruc.h"
#include "dcmtk/dcmnet/dulstruc.h"
#include "dcmtk/dcmnet/dulstruc.h"
#include "dcmtk/dcmnet/dulstruc.h"
#include "dcmtk/dcmnet/dulstruc.h"
#include "dcmtk/dcmnet/helpers.h"
#include "dcmtk/dcmnet/dulstruc.h"
#include "dcmtk/dcmnet/helpers.h"
    if ((assoc->presentationContextList = LST_Create()) == NULL) return EC_MemoryExhausted;
    if ((assoc->userInfo.SCUSCPRoleList = LST_Create()) == NULL) return EC_MemoryExhausted;
                {
                    cond = makeUnderflowError("Application Context item", pduLength, itemLength);
                }
                else
                {
                    DCMNET_TRACE("Successfully parsed Application Context");
                }
            if (context != NULL)
            {
                (void) memset(context, 0, sizeof(*context));
                cond = parsePresentationContext(type, context, buf, &itemLength, pduLength);
                if (cond.bad())
                {
                    free(context);
                }
                else
                {
                    buf += itemLength;
                    if (!OFStandard::safeSubtract(pduLength, itemLength, pduLength))
                    {
                        cond =  makeUnderflowError("Presentation Context item", pduLength, itemLength);
                    }
                    else
                    {
                        LST_Enqueue(&assoc->presentationContextList, (LST_NODE*)context);
                        DCMNET_TRACE("Successfully parsed Presentation Context");
                    }
                }
            }
            else
            {
                cond = EC_MemoryExhausted;
            }
            if (cond.good())
            {
                buf += itemLength;
                if (!OFStandard::safeSubtract(pduLength, itemLength, pduLength))
                {
                    cond = makeUnderflowError("User Information item", pduLength, itemLength);
                }
                else
                {
                    DCMNET_TRACE("Successfully parsed User Information");
                }
            }
            if (cond.good())
            {
                buf += itemLength;
                if (!OFStandard::safeSubtract(pduLength, itemLength, pduLength))
                {
                    cond = makeUnderflowError("unknown item type", pduLength, itemLength);
                }
            }
    if (cond.bad())
    {
      destroyPresentationContextList(&assoc->presentationContextList);
      destroyUserInformationLists(&assoc->userInfo);
    }
                if (cond.bad())
                {
                    free(subItem);
                    return cond;
                }
            if (cond.bad())
            {
                free(role);
                return cond;
            }
#include "dcmtk/dcmnet/dulstruc.h"
/*
 *
 *  Copyright (C) 2021, OFFIS e.V.
 *  All rights reserved.  See COPYRIGHT file for details.
 *
 *  This software and supporting documentation were partly developed by
 *
 *    OFFIS e.V.
 *    R&D Division Health
 *    Escherweg 2
 *    D-26121 Oldenburg, Germany
 *
 *
 *  Module: dcmnet
 *
 *  Author: Michael Onken
 *
 *  Purpose: Collection of helper functions
 *
 */

#include "dcmtk/dcmnet/helpers.h"
#include "dcmtk/dcmnet/lst.h"
#include "dcmtk/dcmnet/assoc.h"
#include "dcmtk/dcmnet/dulstruc.h"


void
destroyPresentationContextList(LST_HEAD ** l)
{
    PRV_PRESENTATIONCONTEXTITEM
    * prvCtx;
    DUL_SUBITEM
        * subItem;

    if (*l == NULL)
        return;

    prvCtx = (PRV_PRESENTATIONCONTEXTITEM*)LST_Dequeue(l);
    while (prvCtx != NULL) {
        subItem = (DUL_SUBITEM*)LST_Dequeue(&prvCtx->transferSyntaxList);
        while (subItem != NULL) {
            free(subItem);
            subItem = (DUL_SUBITEM*)LST_Dequeue(&prvCtx->transferSyntaxList);
        }
        LST_Destroy(&prvCtx->transferSyntaxList);
        free(prvCtx);
        prvCtx = (PRV_PRESENTATIONCONTEXTITEM*)LST_Dequeue(l);
    }
    LST_Destroy(l);
}

void
destroyUserInformationLists(DUL_USERINFO * userInfo)
{
    PRV_SCUSCPROLE
    * role;

    role = (PRV_SCUSCPROLE*)LST_Dequeue(&userInfo->SCUSCPRoleList);
    while (role != NULL) {
        free(role);
        role = (PRV_SCUSCPROLE*)LST_Dequeue(&userInfo->SCUSCPRoleList);
    }
    LST_Destroy(&userInfo->SCUSCPRoleList);

    /* extended negotiation */
    delete userInfo->extNegList; userInfo->extNegList = NULL;

    /* user identity negotiation */
    delete userInfo->usrIdent; userInfo->usrIdent = NULL;
}