Skip to content

Commit de58842

Browse files
authored
gh-94215: Add reproducer for segfault in frame_setlineno() (GH-94563)
1 parent 8bbd70b commit de58842

File tree

1 file changed

+104
-6
lines changed

1 file changed

+104
-6
lines changed

Lib/test/test_pdb.py

+104-6
Original file line numberDiff line numberDiff line change
@@ -1407,14 +1407,82 @@ def test_pdb_issue_gh_91742():
14071407
(Pdb) continue
14081408
Author: 'pi' Version: '3.14'
14091409
"""
1410+
1411+
def test_pdb_issue_gh_94215():
1412+
"""See GH-94215
1413+
1414+
Check that frame_setlineno() does not leak references.
1415+
1416+
>>> def test_function():
1417+
... def func():
1418+
... def inner(v): pass
1419+
... inner(
1420+
... 42
1421+
... )
1422+
...
1423+
... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
1424+
... func()
1425+
1426+
>>> reset_Breakpoint()
1427+
>>> with PdbTestInput([ # doctest: +NORMALIZE_WHITESPACE
1428+
... 'step',
1429+
... 'next',
1430+
... 'next',
1431+
... 'jump 3',
1432+
... 'next',
1433+
... 'next',
1434+
... 'jump 3',
1435+
... 'next',
1436+
... 'next',
1437+
... 'jump 3',
1438+
... 'continue'
1439+
... ]):
1440+
... test_function()
1441+
> <doctest test.test_pdb.test_pdb_issue_gh_94215[0]>(9)test_function()
1442+
-> func()
1443+
(Pdb) step
1444+
--Call--
1445+
> <doctest test.test_pdb.test_pdb_issue_gh_94215[0]>(2)func()
1446+
-> def func():
1447+
(Pdb) next
1448+
> <doctest test.test_pdb.test_pdb_issue_gh_94215[0]>(3)func()
1449+
-> def inner(v): pass
1450+
(Pdb) next
1451+
> <doctest test.test_pdb.test_pdb_issue_gh_94215[0]>(4)func()
1452+
-> inner(
1453+
(Pdb) jump 3
1454+
> <doctest test.test_pdb.test_pdb_issue_gh_94215[0]>(3)func()
1455+
-> def inner(v): pass
1456+
(Pdb) next
1457+
> <doctest test.test_pdb.test_pdb_issue_gh_94215[0]>(4)func()
1458+
-> inner(
1459+
(Pdb) next
1460+
> <doctest test.test_pdb.test_pdb_issue_gh_94215[0]>(5)func()
1461+
-> 42
1462+
(Pdb) jump 3
1463+
> <doctest test.test_pdb.test_pdb_issue_gh_94215[0]>(3)func()
1464+
-> def inner(v): pass
1465+
(Pdb) next
1466+
> <doctest test.test_pdb.test_pdb_issue_gh_94215[0]>(4)func()
1467+
-> inner(
1468+
(Pdb) next
1469+
> <doctest test.test_pdb.test_pdb_issue_gh_94215[0]>(5)func()
1470+
-> 42
1471+
(Pdb) jump 3
1472+
> <doctest test.test_pdb.test_pdb_issue_gh_94215[0]>(3)func()
1473+
-> def inner(v): pass
1474+
(Pdb) continue
1475+
"""
1476+
1477+
14101478
@support.requires_subprocess()
14111479
class PdbTestCase(unittest.TestCase):
14121480
def tearDown(self):
14131481
os_helper.unlink(os_helper.TESTFN)
14141482

14151483
@unittest.skipIf(sys.flags.safe_path,
14161484
'PYTHONSAFEPATH changes default sys.path')
1417-
def _run_pdb(self, pdb_args, commands):
1485+
def _run_pdb(self, pdb_args, commands, expected_returncode=0):
14181486
self.addCleanup(os_helper.rmtree, '__pycache__')
14191487
cmd = [sys.executable, '-m', 'pdb'] + pdb_args
14201488
with subprocess.Popen(
@@ -1427,15 +1495,20 @@ def _run_pdb(self, pdb_args, commands):
14271495
stdout, stderr = proc.communicate(str.encode(commands))
14281496
stdout = stdout and bytes.decode(stdout)
14291497
stderr = stderr and bytes.decode(stderr)
1498+
self.assertEqual(
1499+
proc.returncode,
1500+
expected_returncode,
1501+
f"Unexpected return code\nstdout: {stdout}\nstderr: {stderr}"
1502+
)
14301503
return stdout, stderr
14311504

1432-
def run_pdb_script(self, script, commands):
1505+
def run_pdb_script(self, script, commands, expected_returncode=0):
14331506
"""Run 'script' lines with pdb and the pdb 'commands'."""
14341507
filename = 'main.py'
14351508
with open(filename, 'w') as f:
14361509
f.write(textwrap.dedent(script))
14371510
self.addCleanup(os_helper.unlink, filename)
1438-
return self._run_pdb([filename], commands)
1511+
return self._run_pdb([filename], commands, expected_returncode)
14391512

14401513
def run_pdb_module(self, script, commands):
14411514
"""Runs the script code as part of a module"""
@@ -1641,7 +1714,9 @@ def test_issue16180(self):
16411714
script = "def f: pass\n"
16421715
commands = ''
16431716
expected = "SyntaxError:"
1644-
stdout, stderr = self.run_pdb_script(script, commands)
1717+
stdout, stderr = self.run_pdb_script(
1718+
script, commands, expected_returncode=1
1719+
)
16451720
self.assertIn(expected, stdout,
16461721
'\n\nExpected:\n{}\nGot:\n{}\n'
16471722
'Fail to handle a syntax error in the debuggee.'
@@ -1804,7 +1879,9 @@ def test_module_without_a_main(self):
18041879
with open(init_file, 'w'):
18051880
pass
18061881
self.addCleanup(os_helper.rmtree, module_name)
1807-
stdout, stderr = self._run_pdb(['-m', module_name], "")
1882+
stdout, stderr = self._run_pdb(
1883+
['-m', module_name], "", expected_returncode=1
1884+
)
18081885
self.assertIn("ImportError: No module named t_main.__main__",
18091886
stdout.splitlines())
18101887

@@ -1817,7 +1894,9 @@ def test_package_without_a_main(self):
18171894
with open(modpath + '/__init__.py', 'w'):
18181895
pass
18191896
self.addCleanup(os_helper.rmtree, pkg_name)
1820-
stdout, stderr = self._run_pdb(['-m', modpath.replace('/', '.')], "")
1897+
stdout, stderr = self._run_pdb(
1898+
['-m', modpath.replace('/', '.')], "", expected_returncode=1
1899+
)
18211900
self.assertIn(
18221901
"'t_pkg.t_main' is a package and cannot be directly executed",
18231902
stdout)
@@ -2006,6 +2085,25 @@ def test_issue42383(self):
20062085
expected = '(Pdb) The correct file was executed'
20072086
self.assertEqual(stdout.split('\n')[6].rstrip('\r'), expected)
20082087

2088+
@unittest.skip("test crashes, see gh-94215")
2089+
def test_gh_94215_crash(self):
2090+
script = """\
2091+
def func():
2092+
def inner(v): pass
2093+
inner(
2094+
42
2095+
)
2096+
func()
2097+
"""
2098+
commands = textwrap.dedent("""
2099+
break func
2100+
continue
2101+
next
2102+
next
2103+
jump 2
2104+
""")
2105+
stdout, stderr = self.run_pdb_script(script, commands)
2106+
self.assertFalse(stderr)
20092107

20102108
class ChecklineTests(unittest.TestCase):
20112109
def setUp(self):

0 commit comments

Comments
 (0)