From 9afdeb694729a009a3483c6a80d362430ccb9524 Mon Sep 17 00:00:00 2001 From: Alan Karl Date: Tue, 25 Aug 2026 23:55:21 +0000 Subject: [PATCH] dicom-file: free the frame buffer when dcm_frame_create() rejects it dcm_frame_create() validates the pixel description before it takes ownership of the data pointer, and returns NULL on an empty frame, a bad BitsAllocated, PixelRepresentation, or PlanarConfiguration, or an allocation failure. dcm_filehandle_read_frame() ignored that and returned straight away, so the decoded frame buffer was never freed. The leak is the size of the frame data in the file, not a fixed overhead, and it repeats on every call: 386 byte file -> 8 byte(s) leaked in 1 allocation(s) 8 MB file -> 8388608 byte(s) leaked in 1 allocation(s) Both from the same input with PixelRepresentation out of range; a service reading frames from files it did not produce leaks the whole frame each time. Found by the libFuzzer target, replaying a 90 minute run's corpus with LeakSanitizer enabled. --- src/dicom-file.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/dicom-file.c b/src/dicom-file.c index 48647f3..52c8c81 100644 --- a/src/dicom-file.c +++ b/src/dicom-file.c @@ -1389,19 +1389,30 @@ DcmFrame *dcm_filehandle_read_frame(DcmError **error, return NULL; } - return dcm_frame_create(error, - frame_number, - frame_data, - length, - filehandle->desc.rows, - filehandle->desc.columns, - filehandle->desc.samples_per_pixel, - filehandle->desc.bits_allocated, - filehandle->desc.bits_stored, - filehandle->desc.pixel_representation, - filehandle->desc.planar_configuration, - filehandle->desc.photometric_interpretation, - filehandle->desc.transfer_syntax_uid); + /* dcm_frame_create() only takes ownership of frame_data once it has + * accepted the pixel description, so free it ourselves if it rejects + * the frame -- otherwise a file with, say, an out of range + * PixelRepresentation leaks the whole decoded frame on every call. + */ + DcmFrame *frame = dcm_frame_create(error, + frame_number, + frame_data, + length, + filehandle->desc.rows, + filehandle->desc.columns, + filehandle->desc.samples_per_pixel, + filehandle->desc.bits_allocated, + filehandle->desc.bits_stored, + filehandle->desc.pixel_representation, + filehandle->desc.planar_configuration, + filehandle->desc.photometric_interpretation, + filehandle->desc.transfer_syntax_uid); + if (frame == NULL) { + free(frame_data); + return NULL; + } + + return frame; }