diff --git a/ddtrace/internal/datadog/profiling/stack/src/echion/tasks.cc b/ddtrace/internal/datadog/profiling/stack/src/echion/tasks.cc index 15e03f9681a..57c008e75c1 100644 --- a/ddtrace/internal/datadog/profiling/stack/src/echion/tasks.cc +++ b/ddtrace/internal/datadog/profiling/stack/src/echion/tasks.cc @@ -170,9 +170,16 @@ TaskInfo::unwind(EchionSampler& echion, FrameStack& stack, bool using_uvloop) std::stack> coro_frames; // Unwind the coro chain + size_t coro_chain_depth = 0; for (auto py_coro = this->coro.get(); py_coro != NULL; py_coro = py_coro->await.get()) { - if (py_coro->frame != NULL) + coro_chain_depth++; + if (coro_chain_depth > MAX_RECURSION_DEPTH) { + break; + } + + if (py_coro->frame != NULL) { coro_frames.push(py_coro->frame); + } } // Total number of frames added to the Stack diff --git a/ddtrace/internal/datadog/profiling/stack/src/echion/threads.cc b/ddtrace/internal/datadog/profiling/stack/src/echion/threads.cc index 7e3751cbff7..646ce9c8bff 100644 --- a/ddtrace/internal/datadog/profiling/stack/src/echion/threads.cc +++ b/ddtrace/internal/datadog/profiling/stack/src/echion/threads.cc @@ -211,7 +211,12 @@ ThreadInfo::unwind_tasks(EchionSampler& echion, PyThreadState* tstate) auto stack_info = std::make_unique(leaf_task.get().name, leaf_task.get().is_on_cpu); auto& stack = stack_info->stack; + // Safety: prevent infinite loops from cycles in task chain maps + size_t task_chain_depth = 0; for (auto current_task = leaf_task;;) { + if (++task_chain_depth > MAX_RECURSION_DEPTH) { + break; + } auto& task = current_task.get(); auto task_stack_size = task.unwind(echion, stack, using_uvloop); diff --git a/releasenotes/notes/profiling-fix-max-iterations-unwind-tasks-671d743912c7d600.yaml b/releasenotes/notes/profiling-fix-max-iterations-unwind-tasks-671d743912c7d600.yaml new file mode 100644 index 00000000000..f3f0c6cfc94 --- /dev/null +++ b/releasenotes/notes/profiling-fix-max-iterations-unwind-tasks-671d743912c7d600.yaml @@ -0,0 +1,4 @@ +fixes: + - | + profiling: A bug where the Stack Profiler could loop infinitely (and allocate large amounts of memory, + leading to crashes) when sampling ``asyncio`` Tasks has been fixed.