Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.11] gh-94215: Add reproducer for segfault in frame_setlineno() (GH-94563) #94652

Merged
merged 1 commit into from
Jul 7, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 104 additions & 6 deletions Lib/test/test_pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -1407,14 +1407,82 @@ def test_pdb_issue_gh_91742():
(Pdb) continue
Author: 'pi' Version: '3.14'
"""

def test_pdb_issue_gh_94215():
"""See GH-94215

Check that frame_setlineno() does not leak references.

>>> def test_function():
... def func():
... def inner(v): pass
... inner(
... 42
... )
...
... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
... func()

>>> reset_Breakpoint()
>>> with PdbTestInput([ # doctest: +NORMALIZE_WHITESPACE
... 'step',
... 'next',
... 'next',
... 'jump 3',
... 'next',
... 'next',
... 'jump 3',
... 'next',
... 'next',
... 'jump 3',
... 'continue'
... ]):
... test_function()
> <doctest test.test_pdb.test_pdb_issue_gh_94215[0]>(9)test_function()
-> func()
(Pdb) step
--Call--
> <doctest test.test_pdb.test_pdb_issue_gh_94215[0]>(2)func()
-> def func():
(Pdb) next
> <doctest test.test_pdb.test_pdb_issue_gh_94215[0]>(3)func()
-> def inner(v): pass
(Pdb) next
> <doctest test.test_pdb.test_pdb_issue_gh_94215[0]>(4)func()
-> inner(
(Pdb) jump 3
> <doctest test.test_pdb.test_pdb_issue_gh_94215[0]>(3)func()
-> def inner(v): pass
(Pdb) next
> <doctest test.test_pdb.test_pdb_issue_gh_94215[0]>(4)func()
-> inner(
(Pdb) next
> <doctest test.test_pdb.test_pdb_issue_gh_94215[0]>(5)func()
-> 42
(Pdb) jump 3
> <doctest test.test_pdb.test_pdb_issue_gh_94215[0]>(3)func()
-> def inner(v): pass
(Pdb) next
> <doctest test.test_pdb.test_pdb_issue_gh_94215[0]>(4)func()
-> inner(
(Pdb) next
> <doctest test.test_pdb.test_pdb_issue_gh_94215[0]>(5)func()
-> 42
(Pdb) jump 3
> <doctest test.test_pdb.test_pdb_issue_gh_94215[0]>(3)func()
-> def inner(v): pass
(Pdb) continue
"""


@support.requires_subprocess()
class PdbTestCase(unittest.TestCase):
def tearDown(self):
os_helper.unlink(os_helper.TESTFN)

@unittest.skipIf(sys.flags.safe_path,
'PYTHONSAFEPATH changes default sys.path')
def _run_pdb(self, pdb_args, commands):
def _run_pdb(self, pdb_args, commands, expected_returncode=0):
self.addCleanup(os_helper.rmtree, '__pycache__')
cmd = [sys.executable, '-m', 'pdb'] + pdb_args
with subprocess.Popen(
Expand All @@ -1427,15 +1495,20 @@ def _run_pdb(self, pdb_args, commands):
stdout, stderr = proc.communicate(str.encode(commands))
stdout = stdout and bytes.decode(stdout)
stderr = stderr and bytes.decode(stderr)
self.assertEqual(
proc.returncode,
expected_returncode,
f"Unexpected return code\nstdout: {stdout}\nstderr: {stderr}"
)
return stdout, stderr

def run_pdb_script(self, script, commands):
def run_pdb_script(self, script, commands, expected_returncode=0):
"""Run 'script' lines with pdb and the pdb 'commands'."""
filename = 'main.py'
with open(filename, 'w') as f:
f.write(textwrap.dedent(script))
self.addCleanup(os_helper.unlink, filename)
return self._run_pdb([filename], commands)
return self._run_pdb([filename], commands, expected_returncode)

def run_pdb_module(self, script, commands):
"""Runs the script code as part of a module"""
Expand Down Expand Up @@ -1641,7 +1714,9 @@ def test_issue16180(self):
script = "def f: pass\n"
commands = ''
expected = "SyntaxError:"
stdout, stderr = self.run_pdb_script(script, commands)
stdout, stderr = self.run_pdb_script(
script, commands, expected_returncode=1
)
self.assertIn(expected, stdout,
'\n\nExpected:\n{}\nGot:\n{}\n'
'Fail to handle a syntax error in the debuggee.'
Expand Down Expand Up @@ -1804,7 +1879,9 @@ def test_module_without_a_main(self):
with open(init_file, 'w'):
pass
self.addCleanup(os_helper.rmtree, module_name)
stdout, stderr = self._run_pdb(['-m', module_name], "")
stdout, stderr = self._run_pdb(
['-m', module_name], "", expected_returncode=1
)
self.assertIn("ImportError: No module named t_main.__main__",
stdout.splitlines())

Expand All @@ -1817,7 +1894,9 @@ def test_package_without_a_main(self):
with open(modpath + '/__init__.py', 'w'):
pass
self.addCleanup(os_helper.rmtree, pkg_name)
stdout, stderr = self._run_pdb(['-m', modpath.replace('/', '.')], "")
stdout, stderr = self._run_pdb(
['-m', modpath.replace('/', '.')], "", expected_returncode=1
)
self.assertIn(
"'t_pkg.t_main' is a package and cannot be directly executed",
stdout)
Expand Down Expand Up @@ -2006,6 +2085,25 @@ def test_issue42383(self):
expected = '(Pdb) The correct file was executed'
self.assertEqual(stdout.split('\n')[6].rstrip('\r'), expected)

@unittest.skip("test crashes, see gh-94215")
def test_gh_94215_crash(self):
script = """\
def func():
def inner(v): pass
inner(
42
)
func()
"""
commands = textwrap.dedent("""
break func
continue
next
next
jump 2
""")
stdout, stderr = self.run_pdb_script(script, commands)
self.assertFalse(stderr)

class ChecklineTests(unittest.TestCase):
def setUp(self):
Expand Down