mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	[3.13] gh-134163: Fix an infinite loop when the process runs out of memory in a try block (GH-138491)
				
					
				
			Signed-off-by: yihong0618 <zouzou0208@gmail.com> Co-authored-by: Peter Bierma <zintensitydev@gmail.com>
This commit is contained in:
		
							parent
							
								
									443d4af2ee
								
							
						
					
					
						commit
						afec6a5460
					
				
					 3 changed files with 38 additions and 1 deletions
				
			
		| 
						 | 
					@ -1843,6 +1843,38 @@ def test_memory_error_in_subinterp(self):
 | 
				
			||||||
        rc, _, err = script_helper.assert_python_ok("-c", code)
 | 
					        rc, _, err = script_helper.assert_python_ok("-c", code)
 | 
				
			||||||
        self.assertIn(b'MemoryError', err)
 | 
					        self.assertIn(b'MemoryError', err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @cpython_only
 | 
				
			||||||
 | 
					    # Python built with Py_TRACE_REFS fail with a fatal error in
 | 
				
			||||||
 | 
					    # _PyRefchain_Trace() on memory allocation error.
 | 
				
			||||||
 | 
					    @unittest.skipIf(support.Py_TRACE_REFS, 'cannot test Py_TRACE_REFS build')
 | 
				
			||||||
 | 
					    def test_exec_set_nomemory_hang(self):
 | 
				
			||||||
 | 
					        import_module("_testcapi")
 | 
				
			||||||
 | 
					        # gh-134163: A MemoryError inside code that was wrapped by a try/except
 | 
				
			||||||
 | 
					        # block would lead to an infinite loop.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # The frame_lasti needs to be greater than 257 to prevent
 | 
				
			||||||
 | 
					        # PyLong_FromLong() from returning cached integers, which
 | 
				
			||||||
 | 
					        # don't require a memory allocation. Prepend some dummy code
 | 
				
			||||||
 | 
					        # to artificially increase the instruction index.
 | 
				
			||||||
 | 
					        warmup_code = "a = list(range(0, 1))\n" * 20
 | 
				
			||||||
 | 
					        user_input = warmup_code + dedent("""
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                import _testcapi
 | 
				
			||||||
 | 
					                _testcapi.set_nomemory(0)
 | 
				
			||||||
 | 
					                b = list(range(1000, 2000))
 | 
				
			||||||
 | 
					            except Exception as e:
 | 
				
			||||||
 | 
					                import traceback
 | 
				
			||||||
 | 
					                traceback.print_exc()
 | 
				
			||||||
 | 
					            """)
 | 
				
			||||||
 | 
					        with SuppressCrashReport():
 | 
				
			||||||
 | 
					            with script_helper.spawn_python('-c', user_input) as p:
 | 
				
			||||||
 | 
					                p.wait()
 | 
				
			||||||
 | 
					                output = p.stdout.read()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertIn(p.returncode, (0, 1))
 | 
				
			||||||
 | 
					        self.assertGreater(len(output), 0)  # At minimum, should not hang
 | 
				
			||||||
 | 
					        self.assertIn(b"MemoryError", output)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class NameErrorTests(unittest.TestCase):
 | 
					class NameErrorTests(unittest.TestCase):
 | 
				
			||||||
    def test_name_error_has_name(self):
 | 
					    def test_name_error_has_name(self):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					Fix a hang when the process is out of memory inside an exception handler.
 | 
				
			||||||
| 
						 | 
					@ -912,7 +912,11 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
 | 
				
			||||||
                int frame_lasti = _PyInterpreterFrame_LASTI(frame);
 | 
					                int frame_lasti = _PyInterpreterFrame_LASTI(frame);
 | 
				
			||||||
                PyObject *lasti = PyLong_FromLong(frame_lasti);
 | 
					                PyObject *lasti = PyLong_FromLong(frame_lasti);
 | 
				
			||||||
                if (lasti == NULL) {
 | 
					                if (lasti == NULL) {
 | 
				
			||||||
                    goto exception_unwind;
 | 
					                    // Instead of going back to exception_unwind (which would cause
 | 
				
			||||||
 | 
					                    // infinite recursion), directly exit to let the original exception
 | 
				
			||||||
 | 
					                    // propagate up and hopefully be handled at a higher level.
 | 
				
			||||||
 | 
					                    _PyFrame_SetStackPointer(frame, stack_pointer);
 | 
				
			||||||
 | 
					                    goto exit_unwind;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                PUSH(lasti);
 | 
					                PUSH(lasti);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue