Skip to content

Commit e322ef8

Browse files
authored
chore(profiling): dont call clear in postfork_child (#19121)
[PROF-15438](https://datadoghq.atlassian.net/jira/software/c/projects/PROF/boards/11?selectedIssue=PROF-15438) workspace-gyuheon-1 ## Description Instead of calling `reset()` (which calls `thread_id_to_span.clear()` and would traverse potentially corrupted pointers inherited from a fork that raced with a mutation), it reconstructs the map in place using `placement-new`. This intentionally leaks the old map's nodes, but this is safe/necessary since inspecting or destroying the inconsistent container would be UB. `clear()` is still used in [stack.cpp](https://github.com/DataDog/dd-trace-py/blob/b25d7df821cfdcf76d49db604a1f36beb2c1d515/ddtrace/internal/datadog/profiling/stack/src/stack.cpp#L44-L55) so we keep it This follows th[e pattern of what we do for](https://github.com/DataDog/dd-trace-py/blob/4e5894b231c777cf4b47c4c49e8a9ab7a9f07868/ddtrace/internal/datadog/profiling/stack/echion/echion/strings.h#L89-L104) `StringTable`: ## Testing <!-- Describe your testing strategy or note what tests are included --> ## Risks <!-- Note any risks associated with this change, or "None" if no risks --> ## Additional Notes <!-- Any other information that would be helpful for reviewers --> [PROF-15438]: https://datadoghq.atlassian.net/browse/PROF-15438?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ Co-authored-by: gyuheon.oh <gyuheon.oh@datadoghq.com>
1 parent 831271e commit e322ef8

1 file changed

Lines changed: 12 additions & 2 deletions

File tree

ddtrace/internal/datadog/profiling/stack/src/thread_span_links.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,19 @@ ThreadSpanLinks::reset()
5252
void
5353
ThreadSpanLinks::postfork_child()
5454
{
55+
auto& instance = get_instance();
5556
// NB placement-new to re-init and leak the mutex because doing anything else is UB
56-
new (&get_instance().mtx) std::mutex();
57-
get_instance().reset();
57+
new (&instance.mtx) std::mutex();
58+
// thread_id_to_span may be in a mid-mutation state if fork raced with
59+
// link_span/unlink_span. Its inherited pointers may be inconsistent,
60+
// so calling clear (or letting the destructor run) would traverse the same
61+
// corrupted linked-list state, which is UB. Instead, reconstruct the map in
62+
// place with placement-new without inspecting its contents. This intentionally
63+
// leaks the old map's heap allocations (nodes/buckets), but that memory belonged
64+
// to the parent's address space snapshot and is a bounded, one-time leak per
65+
// fork in the child; freeing it safely is impossible given the possible
66+
// corruption, so leaking is the correct trade-off.
67+
new (&instance.thread_id_to_span) std::unordered_map<uint64_t, std::unique_ptr<Span>>();
5868
}
5969

6070
} // namespace Datadog

0 commit comments

Comments
 (0)