From 6712270a9baa72dc68f0dc9f9acdb2588e289ca2 Mon Sep 17 00:00:00 2001 From: devdanzin <74280297+devdanzin@users.noreply.github.com> Date: Tue, 31 Mar 2026 23:00:20 -0300 Subject: [PATCH] fix: improve error handling in frame context management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove LZ4F_freeDecompressionContext on failed LZ4F_createDecompressionContext in both create_decompression_context and reset_decompression_context — when creation fails, the context pointer may be invalid and freeing it is UB. - Fix PyErr_SetString with literal "%s" in reset_decompression_context — PyErr_SetString does not do printf-style formatting, so the error message would literally contain "%s" instead of the error code. Removed the inapplicable format specifier. - Add NULL checks in capsule destructors (destroy_compression_context and destroy_decompression_context) — PyCapsule_GetPointer can return NULL if the capsule name doesn't match. Without the check, NULL is dereferenced. Found using cext-review-toolkit (https://github.com/devdanzin/cext-review-toolkit). Co-Authored-By: Claude Opus 4.6 (1M context) --- lz4/frame/_frame.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lz4/frame/_frame.c b/lz4/frame/_frame.c index 440b0b5..8471910 100644 --- a/lz4/frame/_frame.c +++ b/lz4/frame/_frame.c @@ -63,6 +63,8 @@ destroy_compression_context (PyObject * py_context) /* Compatibility with 2.6 via capsulethunk. */ struct compression_context *context = py_context; #endif + if (context == NULL) + return; Py_BEGIN_ALLOW_THREADS LZ4F_freeCompressionContext (context->context); Py_END_ALLOW_THREADS @@ -836,6 +838,8 @@ destroy_decompression_context (PyObject * py_context) /* Compatibility with 2.6 via capsulethunk. */ LZ4F_dctx * context = py_context; #endif + if (context == NULL) + return; Py_BEGIN_ALLOW_THREADS LZ4F_freeDecompressionContext (context); Py_END_ALLOW_THREADS @@ -852,7 +856,6 @@ create_decompression_context (PyObject * Py_UNUSED (self)) if (LZ4F_isError (result)) { Py_BLOCK_THREADS - LZ4F_freeDecompressionContext (context); PyErr_Format (PyExc_RuntimeError, "LZ4F_createDecompressionContext failed with code: %s", LZ4F_getErrorName (result)); @@ -913,7 +916,6 @@ reset_decompression_context (PyObject * Py_UNUSED (self), PyObject * args, result = LZ4F_createDecompressionContext (&context, LZ4F_VERSION); if (LZ4F_isError (result)) { - LZ4F_freeDecompressionContext (context); Py_BLOCK_THREADS PyErr_Format (PyExc_RuntimeError, "LZ4F_createDecompressionContext failed with code: %s", @@ -927,7 +929,7 @@ reset_decompression_context (PyObject * Py_UNUSED (self), PyObject * args, { LZ4F_freeDecompressionContext (context); PyErr_SetString (PyExc_RuntimeError, - "PyCapsule_SetPointer failed with code: %s"); + "PyCapsule_SetPointer failed"); return NULL; } }