Project

General

Profile

Actions

Bug #1241

closed

Double free of palette color LUT

Added by Michael Onken about 5 hours ago. Updated about 5 hours ago.

Status:
Closed
Priority:
Normal
Assignee:
Category:
-
Target version:
-
Start date:
2026-07-03
Due date:
% Done:

0%

Estimated time:
Module:
dcmiod
Operating System:
Compiler:

Description

As reported by quellsec.dev:

DCMTK: double-free of palette-color LUT data in IODPaletteColorLUTModule::checkDataConsistency on a malformed green/blue LUT

Summary

DCMTK's IODPaletteColorLUTModule::checkDataConsistency retrieves the red/green/blue palette color LUT data via getXPaletteColorLookupTableData(data, ...) (each returns a fresh new[] copy) and unconditionally delete[] data after each call. The underlying getUintNDataCopy leaves its out-parameter unchanged on every failure path, and the caller never resets data = NULL between calls. A palette whose red LUT is valid but green (or blue) LUT is malformed therefore frees the red copy twice.

Reachable via a crafted PALETTE COLOR DICOM object. Found by the analyze.sh static cppcheck pass (doubleFree), not by the prior agentic dcmtk audits.

Affected version

  • Target: dcmtk
  • Commit read: 7246c5a9ca64c2d4312774bf40d046e255c00a41
  • Bug is in library code (dcmiod/libsrc/modpalettecolorlut.cc).

Crash

AddressSanitizer: double-free (FREE). Crash site: dcmiod/libsrc/modpalettecolorlut.cc:741 in IODPaletteColorLUTModule::checkDataConsistency (duplicate in the 16-bit branch at :758/:763).

Root cause

The accessor returns a copy and, on failure, does not touch the out-param:

dcmiod/libsrc/modpalettecolorlut.cc:234

bc(cpp). return getUint8DataCopy(DCM_RedPaletteColorLookupTableData, dataCopy, numEntries);

(getUint8DataCopy returns EC_IllegalParameter / IOD_EC_InvalidColorPalette without assigning lutData on numBits/descriptor/entry-count mismatch.)

dcmiod/libsrc/modpalettecolorlut.cc:736-741

bc(cpp). result = getRedPaletteColorLookupTableData(data, redActualNumEntries);
delete[] data; // red copy freed; data now dangling
if (result.good()) {
result = getGreenPaletteColorLookupTableData(data, greenActualNumEntries);
delete[] data; // if green failed, data is still the freed red copy -> double free
}

Reproduction

Call path:

bc. PALETTE COLOR DICOM object -> IODPaletteColorLUTModule::checkDataConsistency
-> getRed...Data(data) ok (new[]) -> delete[] data
-> getGreen...Data(data) FAILS without reassigning data (e.g. entry-count mismatch)
-> delete[] data (double-free of the red copy)

Reproduction status: yes-rebuilt-and-ran (harness-only-abort). A harness drives the real IODPaletteColorLUTModule::checkDataConsistency. ASan reports a double-free (delete[]) at #1 checkDataConsistency modpalettecolorlut.cc:741; the first free is at :737 and the allocation at getUint8DataCopy :879. Reached when a crafted PALETTE COLOR LUT object is parsed. See poc/asan_output.txt. verified in source). needs-dynamic to craft the exact red-valid/green-invalid palette and confirm checkDataConsistency runs on the read/validate path. Discovery: cppcheck.

Proof of Concept

Self-contained. docker build clones the target at the pinned commit and builds it under AddressSanitizer + UndefinedBehaviorSanitizer; docker run feeds the crafted input and reproduces the fault. Save the files below into a poc/ directory and:

bc. docker build -t poc . && docker run --rm poc

Sanitizer output

.dockerignore (crafted input)

build.sh

run.sh

Dockerfile

harness.cc

Impact

Double-free (heap corruption) from a crafted PALETTE COLOR DICOM object. Corruption primitive (crash, potentially exploitable). Both 8-bit and 16-bit branches affected. Severity: high.

Suggested fix

Set data = NULL after each delete[] (and have getUintNDataCopy reset its out-param to NULL on every failure return).

Actions #1

Updated by Michael Onken about 5 hours ago

  • Status changed from New to Closed

Closed with commit 99bae4afd570be5f8167dc1d5f48932c00e7346e.

Actions

Also available in: Atom PDF