From 1c45613e9d7adaaa4ac3e2c77b1e7d216580f544 Mon Sep 17 00:00:00 2001 From: Tomas Roun Date: Sun, 27 Oct 2024 15:24:21 +0100 Subject: [PATCH 1/3] Update Lib/test/crashers --- Lib/test/crashers/mutation_inside_cyclegc.py | 31 --------------- Lib/test/crashers/trace_at_recursion_limit.py | 27 ------------- Lib/test/test_gc.py | 38 +++++++++++++++++++ Lib/test/test_sys_settrace.py | 32 ++++++++++++++++ 4 files changed, 70 insertions(+), 58 deletions(-) delete mode 100644 Lib/test/crashers/mutation_inside_cyclegc.py delete mode 100644 Lib/test/crashers/trace_at_recursion_limit.py diff --git a/Lib/test/crashers/mutation_inside_cyclegc.py b/Lib/test/crashers/mutation_inside_cyclegc.py deleted file mode 100644 index 2b67398bccce23..00000000000000 --- a/Lib/test/crashers/mutation_inside_cyclegc.py +++ /dev/null @@ -1,31 +0,0 @@ - -# The cycle GC collector can be executed when any GC-tracked object is -# allocated, e.g. during a call to PyList_New(), PyDict_New(), ... -# Moreover, it can invoke arbitrary Python code via a weakref callback. -# This means that there are many places in the source where an arbitrary -# mutation could unexpectedly occur. - -# The example below shows list_slice() not expecting the call to -# PyList_New to mutate the input list. (Of course there are many -# more examples like this one.) - - -import weakref - -class A(object): - pass - -def callback(x): - del lst[:] - - -keepalive = [] - -for i in range(100): - lst = [str(i)] - a = A() - a.cycle = a - keepalive.append(weakref.ref(a, callback)) - del a - while lst: - keepalive.append(lst[:]) diff --git a/Lib/test/crashers/trace_at_recursion_limit.py b/Lib/test/crashers/trace_at_recursion_limit.py deleted file mode 100644 index acd863f5509c49..00000000000000 --- a/Lib/test/crashers/trace_at_recursion_limit.py +++ /dev/null @@ -1,27 +0,0 @@ -""" -From http://bugs.python.org/issue6717 - -A misbehaving trace hook can trigger a segfault by exceeding the recursion -limit. -""" -import sys - - -def x(): - pass - -def g(*args): - if True: # change to True to crash interpreter - try: - x() - except: - pass - return g - -def f(): - print(sys.getrecursionlimit()) - f() - -sys.settrace(g) - -f() diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py index 2b3c0d3baddeaf..d0649e1d9fdb98 100644 --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py @@ -1526,6 +1526,44 @@ def test_ast_fini(self): assert_python_ok("-c", code) +class MutationInsideCycleGCTests(unittest.TestCase): + def setUp(self): + # This test requires the gc to be enabled. + gc.enable() + self.addCleanup(gc.disable) + + def test_does_not_crash(self): + # Moved from Lib/test/crashers as it does not crash anymore. + # This test ensures that the code does not start crashing again. + # Original comment: + + # The cycle GC collector can be executed when any GC-tracked object is + # allocated, e.g. during a call to PyList_New(), PyDict_New(), ... + # Moreover, it can invoke arbitrary Python code via a weakref callback. + # This means that there are many places in the source where an arbitrary + # mutation could unexpectedly occur. + + # The example below shows list_slice() not expecting the call to + # PyList_New to mutate the input list. (Of course there are many + # more examples like this one.) + class A(object): + pass + + def callback(x): + del lst[:] + + keepalive = [] + + for i in range(100): + lst = [str(i)] + a = A() + a.cycle = a + keepalive.append(weakref.ref(a, callback)) + del a + while lst: + keepalive.append(lst[:]) + + def setUpModule(): global enabled, debug enabled = gc.isenabled() diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py index 95cf0d1ec2d9ab..d54baebe0f2460 100644 --- a/Lib/test/test_sys_settrace.py +++ b/Lib/test/test_sys_settrace.py @@ -3146,5 +3146,37 @@ def func(arg = 1): sys.settrace(None) +class TestTraceAtRecursionLimit(unittest.TestCase): + def setUp(self): + self.addCleanup(sys.settrace, sys.gettrace()) + + def test_does_not_crash(self): + # Moved from Lib/test/crashers as it does not crash anymore. + # This test ensures that the code does not start crashing again. + # Original comment: + + # From http://bugs.python.org/issue6717 + + # A misbehaving trace hook can trigger a segfault by exceeding the recursion + # limit. + def x(): + pass + + def g(*args): + if True: # change to True to crash interpreter + try: + x() + except: + pass + return g + + def f(): + f() + + sys.settrace(g) + with self.assertRaises(RecursionError): + f() + + if __name__ == "__main__": unittest.main() From 6a7c8d7c3314bfdf361e705cd2aa42a6933a13ea Mon Sep 17 00:00:00 2001 From: Tomas Roun Date: Sun, 3 Nov 2024 16:05:00 +0100 Subject: [PATCH 2/3] Skip gc test on free-threading builds --- Lib/test/test_gc.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py index d0649e1d9fdb98..6eb60d914567e8 100644 --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py @@ -1526,6 +1526,7 @@ def test_ast_fini(self): assert_python_ok("-c", code) +@requires_gil_enabled("This test hangs and occasionally segfaults on the Free-threading build") class MutationInsideCycleGCTests(unittest.TestCase): def setUp(self): # This test requires the gc to be enabled. From 8c2c794d52c2e73224fb3b64558a6ceb13cb35f9 Mon Sep 17 00:00:00 2001 From: Tomas Roun Date: Fri, 15 Nov 2024 20:24:20 +0100 Subject: [PATCH 3/3] Remove misleading comment --- Lib/test/test_sys_settrace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py index d54baebe0f2460..5356e1c56aee97 100644 --- a/Lib/test/test_sys_settrace.py +++ b/Lib/test/test_sys_settrace.py @@ -3163,7 +3163,7 @@ def x(): pass def g(*args): - if True: # change to True to crash interpreter + if True: try: x() except: