From d1b94dbaad4a5d87ed3557dd22924e66f7c6ba85 Mon Sep 17 00:00:00 2001 From: Brij Date: Tue, 3 Mar 2026 18:11:37 -0500 Subject: [PATCH 1/5] Add critical section in functools module for --- Modules/_functoolsmodule.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 5286be0b715fff..be7aece8d302c7 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -194,13 +194,19 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw) if (kw != NULL) { PyObject *key, *val; Py_ssize_t pos = 0; + Py_BEGIN_CRITICAL_SECTION(kw); while (PyDict_Next(kw, &pos, &key, &val)) { if (val == phold) { PyErr_SetString(PyExc_TypeError, "Placeholder cannot be passed as a keyword argument"); +#ifdef Py_GIL_DISABLED + /* Need to release lock in case of error */ + PyCriticalSection_End(&_py_cs); +#endif return NULL; } } + Py_END_CRITICAL_SECTION(); } /* check wrapped function / object */ @@ -487,12 +493,15 @@ partial_vectorcall(PyObject *self, PyObject *const *args, /* Copy pto_keywords with overlapping call keywords merged * Note, tail is already coppied. */ Py_ssize_t pos = 0, i = 0; - while (PyDict_Next(n_merges ? pto_kw_merged : pto->kw, &pos, &key, &val)) { + PyObject *keyword_dict = n_merges ? pto_kw_merged : pto->kw; + Py_BEGIN_CRITICAL_SECTION(keyword_dict); + while (PyDict_Next(keyword_dict, &pos, &key, &val)) { assert(i < pto_nkwds); PyTuple_SET_ITEM(tot_kwnames, i, Py_NewRef(key)); stack[tot_nargs + i] = val; i++; } + Py_END_CRITICAL_SECTION(); assert(i == pto_nkwds); Py_XDECREF(pto_kw_merged); @@ -723,6 +732,7 @@ partial_repr(PyObject *self) } } /* Pack keyword arguments */ + Py_BEGIN_CRITICAL_SECTION(kw); for (i = 0; PyDict_Next(kw, &i, &key, &value);) { /* Prevent key.__str__ from deleting the value. */ Py_INCREF(value); @@ -730,9 +740,14 @@ partial_repr(PyObject *self) key, value)); Py_DECREF(value); if (arglist == NULL) { +#ifdef Py_GIL_DISABLED + /* Need to release lock in case of error */ + PyCriticalSection_End(&_py_cs); +#endif goto done; } } + Py_END_CRITICAL_SECTION(); mod = PyType_GetModuleName(Py_TYPE(pto)); if (mod == NULL) { @@ -1247,10 +1262,12 @@ lru_cache_make_key(PyObject *kwd_mark, PyObject *args, } if (kwds_size) { PyTuple_SET_ITEM(key, key_pos++, Py_NewRef(kwd_mark)); + Py_BEGIN_CRITICAL_SECTION(kwds); for (pos = 0; PyDict_Next(kwds, &pos, &keyword, &value);) { PyTuple_SET_ITEM(key, key_pos++, Py_NewRef(keyword)); PyTuple_SET_ITEM(key, key_pos++, Py_NewRef(value)); } + Py_END_CRITICAL_SECTION(); assert(key_pos == PyTuple_GET_SIZE(args) + kwds_size * 2 + 1); } if (typed) { @@ -1259,10 +1276,12 @@ lru_cache_make_key(PyObject *kwd_mark, PyObject *args, PyTuple_SET_ITEM(key, key_pos++, Py_NewRef(item)); } if (kwds_size) { + Py_BEGIN_CRITICAL_SECTION(kwds); for (pos = 0; PyDict_Next(kwds, &pos, &keyword, &value);) { PyObject *item = (PyObject *)Py_TYPE(value); PyTuple_SET_ITEM(key, key_pos++, Py_NewRef(item)); } + Py_END_CRITICAL_SECTION(); } } assert(key_pos == key_size); From 378493d794fefadd872d69d864e3c5f0ba27cb3e Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Tue, 3 Mar 2026 23:21:41 +0000 Subject: [PATCH 2/5] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2026-03-03-23-21-40.gh-issue-145446.0c-TJX.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2026-03-03-23-21-40.gh-issue-145446.0c-TJX.rst diff --git a/Misc/NEWS.d/next/Library/2026-03-03-23-21-40.gh-issue-145446.0c-TJX.rst b/Misc/NEWS.d/next/Library/2026-03-03-23-21-40.gh-issue-145446.0c-TJX.rst new file mode 100644 index 00000000000000..8455b50e0ae7ad --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-03-03-23-21-40.gh-issue-145446.0c-TJX.rst @@ -0,0 +1 @@ +:mod:`functools` is now safer in free-threaded build when using keywords in func:`functools.partial` From ec87ef966cbfb0a851b675022d3f2a68a889ba1d Mon Sep 17 00:00:00 2001 From: bkap123 <97006829+bkap123@users.noreply.github.com> Date: Tue, 3 Mar 2026 18:36:39 -0500 Subject: [PATCH 3/5] Update 2026-03-03-23-21-40.gh-issue-145446.0c-TJX.rst --- .../next/Library/2026-03-03-23-21-40.gh-issue-145446.0c-TJX.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2026-03-03-23-21-40.gh-issue-145446.0c-TJX.rst b/Misc/NEWS.d/next/Library/2026-03-03-23-21-40.gh-issue-145446.0c-TJX.rst index 8455b50e0ae7ad..96eb0d9ddb07ab 100644 --- a/Misc/NEWS.d/next/Library/2026-03-03-23-21-40.gh-issue-145446.0c-TJX.rst +++ b/Misc/NEWS.d/next/Library/2026-03-03-23-21-40.gh-issue-145446.0c-TJX.rst @@ -1 +1 @@ -:mod:`functools` is now safer in free-threaded build when using keywords in func:`functools.partial` +Now :mod:`functools` is safer in free-threaded build when using keywords in :func:`functools.partial` From 6c786a5540e4121c083600c4e07d19448a103025 Mon Sep 17 00:00:00 2001 From: Brij <97006829+bkap123@users.noreply.github.com> Date: Wed, 4 Mar 2026 15:09:51 -0500 Subject: [PATCH 4/5] error logic change --- Modules/_functoolsmodule.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index be7aece8d302c7..a056349d1c2403 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -194,19 +194,20 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw) if (kw != NULL) { PyObject *key, *val; Py_ssize_t pos = 0; + int error = 0; Py_BEGIN_CRITICAL_SECTION(kw); while (PyDict_Next(kw, &pos, &key, &val)) { if (val == phold) { - PyErr_SetString(PyExc_TypeError, - "Placeholder cannot be passed as a keyword argument"); -#ifdef Py_GIL_DISABLED - /* Need to release lock in case of error */ - PyCriticalSection_End(&_py_cs); -#endif - return NULL; + error = 1; + break; } } Py_END_CRITICAL_SECTION(); + if (error) { + PyErr_SetString(PyExc_TypeError, + "Placeholder cannot be passed as a keyword argument"); + return NULL; + } } /* check wrapped function / object */ @@ -732,6 +733,7 @@ partial_repr(PyObject *self) } } /* Pack keyword arguments */ + int error = 0; Py_BEGIN_CRITICAL_SECTION(kw); for (i = 0; PyDict_Next(kw, &i, &key, &value);) { /* Prevent key.__str__ from deleting the value. */ @@ -740,14 +742,14 @@ partial_repr(PyObject *self) key, value)); Py_DECREF(value); if (arglist == NULL) { -#ifdef Py_GIL_DISABLED - /* Need to release lock in case of error */ - PyCriticalSection_End(&_py_cs); -#endif - goto done; + error = 1; + break; } } Py_END_CRITICAL_SECTION(); + if (error) { + goto done; + } mod = PyType_GetModuleName(Py_TYPE(pto)); if (mod == NULL) { From a2a04286d541d77d5d4ad4f284edca6d56ffcad6 Mon Sep 17 00:00:00 2001 From: Brij <97006829+bkap123@users.noreply.github.com> Date: Tue, 10 Mar 2026 18:03:30 -0400 Subject: [PATCH 5/5] remove critical section for keywords --- Modules/_functoolsmodule.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index a056349d1c2403..da1d4dd1c30599 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -194,20 +194,13 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw) if (kw != NULL) { PyObject *key, *val; Py_ssize_t pos = 0; - int error = 0; - Py_BEGIN_CRITICAL_SECTION(kw); while (PyDict_Next(kw, &pos, &key, &val)) { if (val == phold) { - error = 1; - break; + PyErr_SetString(PyExc_TypeError, + "Placeholder cannot be passed as a keyword argument"); + return NULL; } } - Py_END_CRITICAL_SECTION(); - if (error) { - PyErr_SetString(PyExc_TypeError, - "Placeholder cannot be passed as a keyword argument"); - return NULL; - } } /* check wrapped function / object */ @@ -1264,12 +1257,10 @@ lru_cache_make_key(PyObject *kwd_mark, PyObject *args, } if (kwds_size) { PyTuple_SET_ITEM(key, key_pos++, Py_NewRef(kwd_mark)); - Py_BEGIN_CRITICAL_SECTION(kwds); for (pos = 0; PyDict_Next(kwds, &pos, &keyword, &value);) { PyTuple_SET_ITEM(key, key_pos++, Py_NewRef(keyword)); PyTuple_SET_ITEM(key, key_pos++, Py_NewRef(value)); } - Py_END_CRITICAL_SECTION(); assert(key_pos == PyTuple_GET_SIZE(args) + kwds_size * 2 + 1); } if (typed) { @@ -1278,12 +1269,10 @@ lru_cache_make_key(PyObject *kwd_mark, PyObject *args, PyTuple_SET_ITEM(key, key_pos++, Py_NewRef(item)); } if (kwds_size) { - Py_BEGIN_CRITICAL_SECTION(kwds); for (pos = 0; PyDict_Next(kwds, &pos, &keyword, &value);) { PyObject *item = (PyObject *)Py_TYPE(value); PyTuple_SET_ITEM(key, key_pos++, Py_NewRef(item)); } - Py_END_CRITICAL_SECTION(); } } assert(key_pos == key_size);