From 142b7e5a782e007347704b04fc200e5ce7cd0970 Mon Sep 17 00:00:00 2001 From: "Darryl L. Miles" Date: Wed, 26 Feb 2025 13:41:13 +0000 Subject: [PATCH] tclmagic.c: memleak Tcl ref counting over Eval memory leak API docs indicate the original usage seems ok. Calling Tcl_GetIndexFromObj() is allowed with a refcount == 0. But in actual usage it leaks the Tcl_NewStringObj() memory across the Tcl_EvalObjv() call like the object accounting system doesn't work on that anymore (because the use of Tcl_EvalObjv() disrupted things). Looking at the code nearby it increments refs (for all recently created objs) and decrements around Tcl_EvalEx() for the actual data the 'eval' needs pinned and is working on. If we just repeat this same pattern but with 'objv0' the memory leak is gone. The decrement must wake it up / make it remember what to do. So this required covering all the exit paths to decrement as well. --- tcltk/tclmagic.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tcltk/tclmagic.c b/tcltk/tclmagic.c index a616a400..1108d7bd 100644 --- a/tcltk/tclmagic.c +++ b/tcltk/tclmagic.c @@ -369,6 +369,7 @@ _tcl_dispatch(ClientData clientData, if (!strncmp(argv0, "::", 2)) argv0 += 2; objv0 = Tcl_NewStringObj(argv0, strlen(argv0)); + Tcl_IncrRefCount(objv0); /* this seems needed to ensure it is freed across Tcl_EvalObjv() */ if (Tcl_GetIndexFromObj(interp, objv0, (const char **)conflicts, "overloaded command", 0, &idx) == TCL_OK) { @@ -396,7 +397,10 @@ _tcl_dispatch(ClientData clientData, Tcl_Free((char *)objv); if (result == TCL_OK) + { + Tcl_DecrRefCount(objv0); return result; + } /* The rule is to execute Magic commands for any Tcl command */ /* with the same name that returns an error. However, this */ @@ -420,11 +424,17 @@ _tcl_dispatch(ClientData clientData, slashptr++; if ((dotptr = strrchr(slashptr, '.')) != NULL) + { if (strcmp(dotptr + 1, "mag") && strcmp(dotptr + 1, "gz")) + { + Tcl_DecrRefCount(objv0); return result; + } + } } } } + Tcl_DecrRefCount(objv0); Tcl_ResetResult(interp); if (TxInputRedirect == TX_INPUT_REDIRECTED)