From 8f3f1cd19398b61cdbdd6281a1416a2b30faee98 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Tue, 30 Sep 2025 10:57:53 +0300 Subject: [PATCH] Fixed memory leak in webp backend of imgcodecs WebPMuxAssemble always alloc output buffer and do not reuse provided one. It overrites provided buffer and it's not freed in the previous version. WebPDataClear cannot be used with shared pointer as it does not free the object memory itself. webpFree expects void*, but not const void* that's why const cast is required here. --- modules/imgcodecs/src/grfmt_webp.cpp | 38 ++++++++++++++++++---------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/modules/imgcodecs/src/grfmt_webp.cpp b/modules/imgcodecs/src/grfmt_webp.cpp index 932f08bec1..068be52b8b 100644 --- a/modules/imgcodecs/src/grfmt_webp.cpp +++ b/modules/imgcodecs/src/grfmt_webp.cpp @@ -389,33 +389,43 @@ bool WebPEncoder::write(const Mat& img, const std::vector& params) channels = 3; } - uint8_t *out = NULL; + uint8_t *encoder_out = NULL; size_t size = 0; if (comp_lossless) { if (channels == 3) { - size = WebPEncodeLosslessBGR(image->ptr(), width, height, (int)image->step, &out); + size = WebPEncodeLosslessBGR(image->ptr(), width, height, (int)image->step, &encoder_out); } else if (channels == 4) { - size = WebPEncodeLosslessBGRA(image->ptr(), width, height, (int)image->step, &out); + size = WebPEncodeLosslessBGRA(image->ptr(), width, height, (int)image->step, &encoder_out); } } else { if (channels == 3) { - size = WebPEncodeBGR(image->ptr(), width, height, (int)image->step, quality, &out); + size = WebPEncodeBGR(image->ptr(), width, height, (int)image->step, quality, &encoder_out); } else if (channels == 4) { - size = WebPEncodeBGRA(image->ptr(), width, height, (int)image->step, quality, &out); + size = WebPEncodeBGRA(image->ptr(), width, height, (int)image->step, quality, &encoder_out); } } - WebPData finalData = { out, size }; - if (!m_metadata.empty()) { +#if WEBP_DECODER_ABI_VERSION >= 0x0206 + Ptr out_cleaner(encoder_out, WebPFree); +#else + Ptr out_cleaner(encoder_out, free); +#endif + + uint8_t *out = encoder_out; + uint8_t *muxer_out = nullptr; + + if (!m_metadata.empty()) + { + WebPData muxerData; WebPMux* mux = WebPMuxNew(); WebPData imageData = { out, size }; @@ -442,8 +452,10 @@ bool WebPEncoder::write(const Mat& img, const std::vector& params) WebPMuxSetChunk(mux, "ICCP", &metadata, 1); } - if (WebPMuxAssemble(mux, &finalData) == WEBP_MUX_OK) { - size = finalData.size; + if (WebPMuxAssemble(mux, &muxerData) == WEBP_MUX_OK) { + size = muxerData.size; + muxer_out = const_cast(muxerData.bytes); + out = muxer_out; WebPMuxDelete(mux); } else { @@ -453,9 +465,9 @@ bool WebPEncoder::write(const Mat& img, const std::vector& params) } #if WEBP_DECODER_ABI_VERSION >= 0x0206 - Ptr out_cleaner(out, WebPFree); + Ptr muxer_cleaner(muxer_out, WebPFree); #else - Ptr out_cleaner(out, free); + Ptr muxer_cleaner(muxer_out, free); #endif CV_Assert(size > 0); @@ -463,7 +475,7 @@ bool WebPEncoder::write(const Mat& img, const std::vector& params) if (m_buf) { m_buf->resize(size); - memcpy(&(*m_buf)[0], finalData.bytes, size); + memcpy(&(*m_buf)[0], out, size); bytes_written = size; } else @@ -471,7 +483,7 @@ bool WebPEncoder::write(const Mat& img, const std::vector& params) FILE *fd = fopen(m_filename.c_str(), "wb"); if (fd != NULL) { - bytes_written = fwrite(finalData.bytes, sizeof(uint8_t), size, fd); + bytes_written = fwrite(out, sizeof(uint8_t), size, fd); if (size != bytes_written) { CV_LOG_ERROR(NULL, cv::format("Only %zu or %zu bytes are written\n",bytes_written, size));