CVE-2021-41690 (C++, published 2022-06-28T13:15Z)
Description: value: DCMTK through 3.6.6 does not handle memory free properly. The malloced memory for storing all file information are recorded in a global variable LST and are not freed properly. Sending specific requests to the dcmqrdb program can incur a 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;
}