Re: [PATCH v1 18/58] perf python: Add callchain support

sashiko-bot
2026-04-20T00:48:38Z

Sashiko AI review found 4 potential issue(s):
- [Critical] Lazy evaluation of callchain resolution accesses a transient mmap buffer after it may have been overwritten or unmapped.
- [High] Missing NULL pointer check for `evsel` causes a deterministic crash on uninitialized or malformed events.
- [High] Potential Use-After-Free (UAF) of `evlist` and `session` when accessing `evsel->evlist` during deferred evaluation.
- [Medium] Unprotected state modifications of global `symbol_conf` in instance initialization.
--

commit c812b2453db1f3bc8f2944a3e0a29148617b9c48
Author: Ian Rogers <irogers@google.com>

perf python: Add callchain support

Implement pyrf_callchain_node and pyrf_callchain types for lazy
iteration over callchain frames. Add callchain property to
sample_event.

> diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
> index 09b5667075638..6baf38a086901 100644
> --- a/tools/perf/util/python.c
> +++ b/tools/perf/util/python.c

[ ... ]

> +static PyObject *pyrf_callchain__next(struct pyrf_callchain *pchain)
> +{
> + struct pyrf_callchain_node *pnode;
> +
> + if (!pchain->resolved) {
> + struct evsel *evsel = pchain->pevent->sample.evsel;
> + struct evlist *evlist = evsel->evlist;

If the event is manually instantiated in Python or yielded from a context
where the evsel lookup failed, evsel might be NULL. Could this cause a
NULL pointer dereference when accessing evsel->evlist?

> + struct perf_session *session = evlist ? evlist->session : NULL;

The pyrf_event object holds a reference count to its evsel, keeping it
alive even if the parent evlist and perf_session are closed or garbage
collected.

Since evsel does not hold a strong reference back to evlist, could
evsel->evlist be a dangling pointer here once the evlist is destroyed?

If the Python script has dropped the session but retained the event,
would accessing evlist->session dereference freed memory?

> + struct addr_location al;
> + struct callchain_cursor *cursor;
> + struct callchain_cursor_node *node;
> + u64 i;
> +
> + if (!session || !pchain->pevent->sample.callchain)
> + return NULL;
> +
> + addr_location__init(&al);
> + if (machine__resolve(&session->machines.host, &al, &pchain->pevent->sample) < 0) {
> + addr_location__exit(&al);
> + return NULL;
> + }
> +
> + cursor = get_tls_callchain_cursor();
> + if (thread__resolve_callchain(al.thread, cursor, evsel,
> + &pchain->pevent->sample, NULL, NULL,
> + PERF_MAX_STACK_DEPTH) != 0) {

The callchain property defers resolution until iteration.

However, pchain->pevent->sample.callchain points directly into the
kernel's mmap ring buffer. If the Python script stores the callchain
and iterates it outside the immediate event callback, the underlying mmap
buffer might have already advanced or been unmapped.

Could this deferred parsing cause thread__resolve_callchain() to parse
overwritten memory or trigger a segfault? Would it be safer to perform
callchain resolution eagerly when the event is processed?

[ ... ]

> @@ -2836,6 +3035,9 @@ static int pyrf_session__init(struct pyrf_session *psession, PyObject *args, PyO
> return -1;
> }
>
> + symbol_conf.use_callchain = true;
> + symbol_conf.show_kernel_path = true;
> + symbol_conf.inline_name = false;
> if (symbol__init(perf_session__env(psession->session)) < 0) {

symbol_conf is a process-wide global configuration structure.

Does modifying it unconditionally in pyrf_session__init() mean that
initializing a new Python perf.session instance could overwrite the global
configuration for all other concurrent sessions in the same Python process?

--
Sashiko AI review · https://sashiko.dev/#/patchset/20260419235911.2186050-1-irogers@google.com?part=18