CVE-2021-41687 (C++, published 2022-06-28T13:15Z)
Description: value: DCMTK through 3.6.6 does not handle memory free properly. The program malloc a heap memory for parsing data, but does not free it when error in parsing. Sending specific requests to the dcmqrdb program incur the memory leak. An attacker can use it to launch a DoS attack.
Severity: MEDIUM (CVSS v3 base score: 7.5)
Weakness: CWE-401 \u2014 Missing Release of Memory after Effective Lifetime
The product does not sufficiently track and release allocated memory after it has been used, which slowly consumes remaining memory.
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;
}