From 3a39f9a22d2c48c1283bb7b4580e742778229128 Mon Sep 17 00:00:00 2001 From: Timofey Ivankov Date: Sat, 19 Sep 2026 14:11:22 +0300 Subject: [PATCH 1/2] gh-157788: Fix python -m asyncio ps failing on long task names --- Lib/test/test_external_inspection.py | 40 +++++++++++++++++++ ...-09-19-14-10-18.gh-issue-157788.v70Lxd.rst | 2 + Modules/_remote_debugging/object_reading.c | 6 ++- 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-09-19-14-10-18.gh-issue-157788.v70Lxd.rst diff --git a/Lib/test/test_external_inspection.py b/Lib/test/test_external_inspection.py index 910fe96d5e7d81e..57a8d0d190c5462 100644 --- a/Lib/test/test_external_inspection.py +++ b/Lib/test/test_external_inspection.py @@ -1,3 +1,4 @@ +import asyncio import unittest import os import textwrap @@ -440,6 +441,45 @@ def _extract_coroutine_stacks_lineno_only(self, stack_trace): @requires_remote_subprocess_debugging() class TestSelfStackTrace(RemoteInspectionTestBase): + @skip_if_not_supported + def test_long_task_name_is_truncated(self): + # gh-157788 + async def main(): + asyncio.create_task(asyncio.sleep(10_000), name="x" * 300) + await asyncio.sleep(0) + return [ + task.task_name + for info in RemoteUnwinder(os.getpid()).get_all_awaited_by() + for task in info.awaited_by + ] + + names = asyncio.run(main()) + self.assertIn("Task-1", names) + self.assertEqual([len(n) for n in names if n.startswith("x")], [255]) + + @skip_if_not_supported + def test_negative_string_length_is_rejected(self): + # gh-157788 + import ctypes + + async def main(): + name = "corrupted" + "x" * 20 + asyncio.create_task(asyncio.sleep(10_000), name=name) + await asyncio.sleep(0) + + length = ctypes.c_ssize_t.from_address( + id(name) + object().__sizeof__()) + real_length = length.value + length.value = -1 + try: + with self.assertRaisesRegex(RuntimeError, + "Invalid string length"): + RemoteUnwinder(os.getpid()).get_all_awaited_by() + finally: + length.value = real_length + + asyncio.run(main()) + @skip_if_not_supported @unittest.skipIf( sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, diff --git a/Misc/NEWS.d/next/Library/2026-09-19-14-10-18.gh-issue-157788.v70Lxd.rst b/Misc/NEWS.d/next/Library/2026-09-19-14-10-18.gh-issue-157788.v70Lxd.rst new file mode 100644 index 000000000000000..641ca0a82a0915e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-09-19-14-10-18.gh-issue-157788.v70Lxd.rst @@ -0,0 +1,2 @@ +Fix ``python -m asyncio ps`` failing on a process that has a task with a +name longer than 255 characters. diff --git a/Modules/_remote_debugging/object_reading.c b/Modules/_remote_debugging/object_reading.c index 1cea96a2151fcc6..56d9f80a80fd0f5 100644 --- a/Modules/_remote_debugging/object_reading.c +++ b/Modules/_remote_debugging/object_reading.c @@ -64,12 +64,16 @@ read_py_str( } Py_ssize_t len = GET_MEMBER(Py_ssize_t, unicode_obj, unwinder->debug_offsets.unicode_object.length); - if (len < 0 || len > max_len) { + if (len < 0) { PyErr_Format(PyExc_RuntimeError, "Invalid string length (%zd) at 0x%lx", len, address); set_exception_cause(unwinder, PyExc_RuntimeError, "Invalid string length in remote Unicode object"); return NULL; } + if (len > max_len) { + // gh-157788: a long name must not fail the whole read + len = max_len; + } // Inspect state to pick the right data offset and character width. // We rely on the remote process sharing this Python version's From 4ecf22da003f2d654480f14970b2ba657d7ecda3 Mon Sep 17 00:00:00 2001 From: Timofey Ivankov Date: Sat, 19 Sep 2026 21:38:12 +0300 Subject: [PATCH 2/2] remove test --- Lib/test/test_external_inspection.py | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/Lib/test/test_external_inspection.py b/Lib/test/test_external_inspection.py index 57a8d0d190c5462..1fed0f9175e8e10 100644 --- a/Lib/test/test_external_inspection.py +++ b/Lib/test/test_external_inspection.py @@ -457,29 +457,6 @@ async def main(): self.assertIn("Task-1", names) self.assertEqual([len(n) for n in names if n.startswith("x")], [255]) - @skip_if_not_supported - def test_negative_string_length_is_rejected(self): - # gh-157788 - import ctypes - - async def main(): - name = "corrupted" + "x" * 20 - asyncio.create_task(asyncio.sleep(10_000), name=name) - await asyncio.sleep(0) - - length = ctypes.c_ssize_t.from_address( - id(name) + object().__sizeof__()) - real_length = length.value - length.value = -1 - try: - with self.assertRaisesRegex(RuntimeError, - "Invalid string length"): - RemoteUnwinder(os.getpid()).get_all_awaited_by() - finally: - length.value = real_length - - asyncio.run(main()) - @skip_if_not_supported @unittest.skipIf( sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED,