From 0919dec7fae523b39e6d17831a60780bc1b18e27 Mon Sep 17 00:00:00 2001 From: tangyuan0821 Date: Mon, 18 Aug 2025 16:07:54 +0800 Subject: [PATCH 1/8] Fix segmentation fault in deeply nested filter() iterator chains --- Python/bltinmodule.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 2c4bf46d85b0ba..c2e3c4e84b93e9 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -579,6 +579,14 @@ filter_traverse(PyObject *self, visitproc visit, void *arg) return 0; } +static int +filter_clear(filterobject *lz) +{ + Py_CLEAR(lz->it); + Py_CLEAR(lz->func); + return 0; +} + static PyObject * filter_next(PyObject *self) { @@ -660,8 +668,8 @@ PyTypeObject PyFilter_Type = { Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /* tp_flags */ filter_doc, /* tp_doc */ - filter_traverse, /* tp_traverse */ - 0, /* tp_clear */ + (traverseproc)filter_traverse, /* tp_traverse */ + (inquiry)filter_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ From 21a392400a25a44fce2ce8c051d90857a8eccbe3 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Mon, 18 Aug 2025 08:14:54 +0000 Subject: [PATCH 2/8] =?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 --- .../2025-08-18-08-14-52.gh-issue-137894.SrkIA_.rst | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-18-08-14-52.gh-issue-137894.SrkIA_.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-08-18-08-14-52.gh-issue-137894.SrkIA_.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-08-18-08-14-52.gh-issue-137894.SrkIA_.rst new file mode 100644 index 00000000000000..e0c6f785add31d --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-08-18-08-14-52.gh-issue-137894.SrkIA_.rst @@ -0,0 +1,6 @@ +Fix segmentation fault in deeply nested :func:`filter` iterator chains by +adding a ``tp_clear`` method to the filter object. Previously, creating +thousands of nested filter iterators and then consuming them would cause +a stack overflow during garbage collection, leading to interpreter crashes. +Now such cases properly raise :exc:`RecursionError` instead of crashing +the interpreter. From 61a050695e8e19f16a3452c4f1685f64669b8dd8 Mon Sep 17 00:00:00 2001 From: tangyuan0821 Date: Mon, 18 Aug 2025 17:21:58 +0800 Subject: [PATCH 3/8] Add tests for filter object recursion and garbage collection handling --- Lib/test/test_builtin.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 8830641f0abdc7..6f3eddab14b0f6 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1132,6 +1132,22 @@ def test_filter_dealloc(self): del i gc.collect() + @support.skip_wasi_stack_overflow() + @support.skip_emscripten_stack_overflow() + @support.requires_resource('cpu') + def test_filter_deep_nesting_recursion_error(self): + # gh-137894: Test that deeply nested filter() iterator chains + # raise RecursionError instead of causing segmentation fault. + # This verifies that the tp_clear method prevents stack overflow + # during garbage collection of cyclic references. + i = filter(bool, range(1000000)) + for _ in range(100000): + i = filter(bool, i) + + # Should raise RecursionError, not segmentation fault + with self.assertRaises(RecursionError): + list(i) + def test_getattr(self): self.assertTrue(getattr(sys, 'stdout') is sys.stdout) self.assertRaises(TypeError, getattr) From 1e6340922f2b9edcf055728cffb6dd5cb5b788ea Mon Sep 17 00:00:00 2001 From: tangyuan0821 Date: Mon, 18 Aug 2025 17:26:29 +0800 Subject: [PATCH 4/8] Removed trailing whitespace from test_builtin.py --- Lib/test/test_builtin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 6f3eddab14b0f6..e282c3eaf2ee1c 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1143,7 +1143,7 @@ def test_filter_deep_nesting_recursion_error(self): i = filter(bool, range(1000000)) for _ in range(100000): i = filter(bool, i) - + # Should raise RecursionError, not segmentation fault with self.assertRaises(RecursionError): list(i) From 413beaa037ea4d97bf3b5daebbd50d4b2857e406 Mon Sep 17 00:00:00 2001 From: tangyuan0821 Date: Mon, 18 Aug 2025 18:02:25 +0800 Subject: [PATCH 5/8] Fix filter_clear signature and address review feedback --- Lib/test/test_builtin.py | 2 -- .../2025-08-18-08-14-52.gh-issue-137894.SrkIA_.rst | 9 +++------ Python/bltinmodule.c | 5 +++-- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index e282c3eaf2ee1c..cda1b563ace38e 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1132,8 +1132,6 @@ def test_filter_dealloc(self): del i gc.collect() - @support.skip_wasi_stack_overflow() - @support.skip_emscripten_stack_overflow() @support.requires_resource('cpu') def test_filter_deep_nesting_recursion_error(self): # gh-137894: Test that deeply nested filter() iterator chains diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-08-18-08-14-52.gh-issue-137894.SrkIA_.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-08-18-08-14-52.gh-issue-137894.SrkIA_.rst index e0c6f785add31d..55bebff0aa822e 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-08-18-08-14-52.gh-issue-137894.SrkIA_.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-08-18-08-14-52.gh-issue-137894.SrkIA_.rst @@ -1,6 +1,3 @@ -Fix segmentation fault in deeply nested :func:`filter` iterator chains by -adding a ``tp_clear`` method to the filter object. Previously, creating -thousands of nested filter iterators and then consuming them would cause -a stack overflow during garbage collection, leading to interpreter crashes. -Now such cases properly raise :exc:`RecursionError` instead of crashing -the interpreter. +Fix segmentation fault in deeply nested :func:`filter` iterator chains. +Deeply nested filter iterators now properly raise :exc:`RecursionError` +instead of crashing the interpreter. diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index c2e3c4e84b93e9..2978087c7d7a08 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -580,8 +580,9 @@ filter_traverse(PyObject *self, visitproc visit, void *arg) } static int -filter_clear(filterobject *lz) +filter_clear(PyObject *self) { + filterobject *lz = _filterobject_CAST(self); Py_CLEAR(lz->it); Py_CLEAR(lz->func); return 0; @@ -668,7 +669,7 @@ PyTypeObject PyFilter_Type = { Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /* tp_flags */ filter_doc, /* tp_doc */ - (traverseproc)filter_traverse, /* tp_traverse */ + filter_traverse, /* tp_traverse */ (inquiry)filter_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ From 02569041b124317ba3758af38ac257834b52a0a7 Mon Sep 17 00:00:00 2001 From: tangyuan0821 Date: Mon, 18 Aug 2025 18:17:28 +0800 Subject: [PATCH 6/8] Fix the signature of filter_clear to solve the problem in the RecursionError test --- Lib/test/test_builtin.py | 6 ++---- Python/bltinmodule.c | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index cda1b563ace38e..d388b7b1ff246f 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1132,7 +1132,7 @@ def test_filter_dealloc(self): del i gc.collect() - @support.requires_resource('cpu') + def test_filter_deep_nesting_recursion_error(self): # gh-137894: Test that deeply nested filter() iterator chains # raise RecursionError instead of causing segmentation fault. @@ -1142,9 +1142,7 @@ def test_filter_deep_nesting_recursion_error(self): for _ in range(100000): i = filter(bool, i) - # Should raise RecursionError, not segmentation fault - with self.assertRaises(RecursionError): - list(i) + self.assertRaises(RecursionError, list, i) def test_getattr(self): self.assertTrue(getattr(sys, 'stdout') is sys.stdout) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 2978087c7d7a08..3529707b66c05f 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -670,7 +670,7 @@ PyTypeObject PyFilter_Type = { Py_TPFLAGS_BASETYPE, /* tp_flags */ filter_doc, /* tp_doc */ filter_traverse, /* tp_traverse */ - (inquiry)filter_clear, /* tp_clear */ + filter_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ From 757bd88f26460c95634f2c18670475fb41297c30 Mon Sep 17 00:00:00 2001 From: tangyuan0821 Date: Mon, 18 Aug 2025 18:19:50 +0800 Subject: [PATCH 7/8] Fix the blank line issue in test_builtin.py --- Lib/test/test_builtin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index d388b7b1ff246f..46d2963ce20a69 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1132,7 +1132,7 @@ def test_filter_dealloc(self): del i gc.collect() - + def test_filter_deep_nesting_recursion_error(self): # gh-137894: Test that deeply nested filter() iterator chains # raise RecursionError instead of causing segmentation fault. From 95def8ee1eb98a054bba7f46a333d5d49d1d2406 Mon Sep 17 00:00:00 2001 From: tangyuan0821 Date: Mon, 18 Aug 2025 19:25:32 +0800 Subject: [PATCH 8/8] Add skip conditions for test_filter_deep_nesting_recursion_error and update the cleanup function of filter_clear --- Lib/test/test_builtin.py | 7 ++++--- Python/bltinmodule.c | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 46d2963ce20a69..80ba9a7ae61b64 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1121,7 +1121,6 @@ def test_filter_pickle(self): @support.skip_wasi_stack_overflow() @support.skip_emscripten_stack_overflow() - @support.requires_resource('cpu') def test_filter_dealloc(self): # Tests recursive deallocation of nested filter objects using the # thrashcan mechanism. See gh-102356 for more details. @@ -1131,8 +1130,10 @@ def test_filter_dealloc(self): i = filter(bool, i) del i gc.collect() - - + 6 + @support.skip_wasi_stack_overflow() + @support.skip_emscripten_stack_overflow() + @support.requires_resource('cpu') def test_filter_deep_nesting_recursion_error(self): # gh-137894: Test that deeply nested filter() iterator chains # raise RecursionError instead of causing segmentation fault. diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 3529707b66c05f..2a6fe6cfd836d6 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -670,7 +670,7 @@ PyTypeObject PyFilter_Type = { Py_TPFLAGS_BASETYPE, /* tp_flags */ filter_doc, /* tp_doc */ filter_traverse, /* tp_traverse */ - filter_clear, /* tp_clear */ + filter_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */