From 5867f192dade717c4bdd1fe63d438dc2548c5b2f Mon Sep 17 00:00:00 2001 From: devdanzin <74280297+devdanzin@users.noreply.github.com> Date: Tue, 31 Mar 2026 22:55:43 -0300 Subject: [PATCH] fix: release buffers and free memory on error paths Fix memory and buffer leaks on five error paths: - block/compress: release source and dict Py_buffers on PyMem_Malloc failure - block/decompress: release source and dict Py_buffers on PyMem_Malloc failure - frame/compress: release source Py_buffer on block_checksum version error - frame/compress_begin: free destination buffer on LZ4F_compressBegin failure (same pattern as the bug fixed in 43fe65d, not propagated to this site) - frame/__decompress: free original destination buffer on PyMem_Realloc failure (realloc returns NULL without freeing the original pointer) Found using cext-review-toolkit (https://github.com/devdanzin/cext-review-toolkit). Co-Authored-By: Claude Opus 4.6 (1M context) --- lz4/block/_block.c | 4 ++++ lz4/frame/_frame.c | 3 +++ 2 files changed, 7 insertions(+) diff --git a/lz4/block/_block.c b/lz4/block/_block.c index 993cc44..4b7dc38 100644 --- a/lz4/block/_block.c +++ b/lz4/block/_block.c @@ -215,6 +215,8 @@ compress (PyObject * Py_UNUSED (self), PyObject * args, PyObject * kwargs) dest = PyMem_Malloc (total_size * sizeof * dest); if (dest == NULL) { + PyBuffer_Release(&source); + PyBuffer_Release(&dict); return PyErr_NoMemory(); } @@ -349,6 +351,8 @@ decompress (PyObject * Py_UNUSED (self), PyObject * args, PyObject * kwargs) dest = PyMem_Malloc (dest_size * sizeof * dest); if (dest == NULL) { + PyBuffer_Release(&source); + PyBuffer_Release(&dict); return PyErr_NoMemory(); } diff --git a/lz4/frame/_frame.c b/lz4/frame/_frame.c index 440b0b5..eb1c76b 100644 --- a/lz4/frame/_frame.c +++ b/lz4/frame/_frame.c @@ -184,6 +184,7 @@ compress (PyObject * Py_UNUSED (self), PyObject * args, } else if (block_checksum) { + PyBuffer_Release(&source); PyErr_SetString (PyExc_RuntimeError, "block_checksum specified but not supported by LZ4 library version"); return NULL; @@ -383,6 +384,7 @@ compress_begin (PyObject * Py_UNUSED (self), PyObject * args, if (LZ4F_isError (result)) { + PyMem_Free (destination); PyErr_Format (PyExc_RuntimeError, "LZ4F_compressBegin failed with code: %s", LZ4F_getErrorName (result)); @@ -1115,6 +1117,7 @@ __decompress(LZ4F_dctx * context, char * source, size_t source_size, buff = PyMem_Realloc (destination, destination_size); if (buff == NULL) { + PyMem_Free (destination); PyErr_SetString (PyExc_RuntimeError, "Failed to resize buffer"); return NULL;