Skip to content

RemoteUnwinder.get_stack_trace is missing exceptions on some error code paths #144316

@colesbury

Description

@colesbury

Bug report

On a PR recently, I saw the following crash in test_external_inspection. I don't think the crash is related to the PR:

Test that frame cache is per-thread and cache invalidation works independently. ... Assertion failed: (res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL), file C:\a\cpython\cpython\Python\generated_cases.c.h, line 3938
Fatal Python error: Aborted

<Cannot show all threads while the GIL is disabled>
Stack (most recent call first):
  File "C:\a\cpython\cpython\Lib\test\test_external_inspection.py", line 2702 in _get_frames_with_retry
  File "C:\a\cpython\cpython\Lib\test\test_external_inspection.py", line 3268 in test_cache_per_thread_isolation
  File "C:\a\cpython\cpython\Lib\unittest\case.py", line 613 in _callTestMethod
  File "C:\a\cpython\cpython\Lib\unittest\case.py", line 667 in run
  File "C:\a\cpython\cpython\Lib\unittest\case.py", line 723 in __call__
  File "C:\a\cpython\cpython\Lib\unittest\suite.py", line 122 in run
  File "C:\a\cpython\cpython\Lib\unittest\suite.py", line 84 in __call__
  File "C:\a\cpython\cpython\Lib\unittest\suite.py", line 122 in run
  File "C:\a\cpython\cpython\Lib\unittest\suite.py", line 84 in __call__
  File "C:\a\cpython\cpython\Lib\unittest\runner.py", line 257 in run
  File "C:\a\cpython\cpython\Lib\test\libregrtest\single.py", line 84 in _run_suite
  File "C:\a\cpython\cpython\Lib\test\libregrtest\single.py", line 42 in run_unittest
  File "C:\a\cpython\cpython\Lib\test\libregrtest\single.py", line 162 in test_func
  File "C:\a\cpython\cpython\Lib\test\libregrtest\single.py", line 118 in regrtest_runner
  File "C:\a\cpython\cpython\Lib\test\libregrtest\single.py", line 165 in _load_run_test
  File "C:\a\cpython\cpython\Lib\test\libregrtest\single.py", line 210 in _runtest_env_changed_exc
  File "C:\a\cpython\cpython\Lib\test\libregrtest\single.py", line 319 in _runtest
  File "C:\a\cpython\cpython\Lib\test\libregrtest\single.py", line 348 in run_single_test
  File "C:\a\cpython\cpython\Lib\test\libregrtest\worker.py", line 99 in worker_process
  File "C:\a\cpython\cpython\Lib\test\libregrtest\worker.py", line 134 in main
  File "C:\a\cpython\cpython\Lib\test\libregrtest\worker.py", line 138 in <module>
  File "C:\a\cpython\cpython\Lib\runpy.py", line 87 in _run_code
  File "C:\a\cpython\cpython\Lib\runpy.py", line 196 in _run_module_as_main

I think that's in the call to _remote_debugging.RemoteUnwinder.get_stack_trace. Either it's returning NULL without an exception set or it's returning non-NULL with an exception.

I asked Claude to find the missing exceptions and it came up with the following:

  Missing Exception Error Paths

  1. asyncio.c:119-122 - Set object validation
  if (mask < 0 || mask >= MAX_SET_TABLE_SIZE || num_els < 0 || num_els > mask + 1) {
      set_exception_cause(unwinder, PyExc_RuntimeError,
          "Invalid set object (corrupted remote memory)");
      return -1;
  }
  2. object_reading.c:198-201 - PyLong size validation
  if (size < 0 || size > MAX_LONG_DIGITS) {
      set_exception_cause(unwinder, PyExc_RuntimeError,
          "Invalid PyLong size (corrupted remote memory)");
      return -1;
  }
  3. frames.c:50-54 - Stack chunk size validation
  if (actual_size <= offsetof(_PyStackChunk, data) || actual_size > MAX_STACK_CHUNK_SIZE) {
      PyMem_RawFree(this_chunk);
      set_exception_cause(unwinder, PyExc_RuntimeError,
          "Invalid stack chunk size (corrupted remote memory)");
      return -1;
  }
  4. code_objects.c:448-451 - TLBC index validation
  if (ctx->tlbc_index < 0 || ctx->tlbc_index >= tlbc_entry->tlbc_array_size) {
      set_exception_cause(unwinder, PyExc_RuntimeError,
          "Invalid tlbc_index (corrupted remote memory)");
      goto error;
  }
  5. module.c:596-601 - Thread list cycle detection
  if (current_tstate == prev_tstate) {
      Py_DECREF(interpreter_threads);
      set_exception_cause(self, PyExc_RuntimeError,
          "Thread list cycle detected (corrupted remote memory)");
      Py_CLEAR(result);
      goto exit;
  }

The set_exception_cause macro only sets an exception if unwinder->debug is set.

cc @pablogsal

Metadata

Metadata

Assignees

No one assigned

    Labels

    interpreter-core(Objects, Python, Grammar, and Parser dirs)topic-profilingtype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions