Re: [PATCH v1 21/58] perf python: Expose brstack in sample event

sashiko-bot
2026-04-20T00:34:56Z

Sashiko AI review found 3 potential issue(s):
- [Medium] Use-after-free and out-of-bounds read when iterating `brstack` outside the event callback.
- [Medium] API Usability conflict: `.from` property uses a reserved Python keyword.
- [Low] Memory leak on error path due to unchecked `PyModule_AddObject` return value.
--

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

perf python: Expose brstack in sample event

Implement pyrf_branch_entry and pyrf_branch_stack for lazy
iteration over branch stack entries.

> diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
> index 1e393f354ea0b..52970c78106f6 100644
> --- a/tools/perf/util/python.c
> +++ b/tools/perf/util/python.c
[ ... ]
> @@ -866,6 +867,150 @@ static PyObject *pyrf_sample_event__get_callchain(PyObject *self, void */*closur
[ ... ]
> +static PyGetSetDef pyrf_branch_entry__getset[] = {
> + { .name = "from", .get = (getter)pyrf_branch_entry__get_from, },

Does exposing the branch origin as 'from' cause a SyntaxError in Python since
it is a reserved keyword? This might force users to unidiomatically rely on
getattr(entry, 'from').

> + { .name = "to", .get = (getter)pyrf_branch_entry__get_to, },
> + { .name = "mispred", .get = (getter)pyrf_branch_entry__get_mispred, },
[ ... ]
> +static PyObject *pyrf_branch_stack__next(struct pyrf_branch_stack *pstack)
> +{
> + struct pyrf_branch_entry *pentry;
> + struct branch_stack *bs = pstack->pevent->sample.branch_stack;
> + struct branch_entry *entries = perf_sample__branch_entries(&pstack->pevent->sample);
> +
> + if (!bs || !entries || pstack->pos >= bs->nr)
> + return NULL;

Can this regression lead to reading freed memory if the stack is iterated
outside the event callback?

Since pevent->sample.branch_stack is a shallow copy pointing to the original
event buffer, if a Python script stores the event object and iterates it
lazily later, the original buffer might be overwritten or unmapped.

If the overwritten memory yields a large value for bs->nr, could this bypass
the loop bound and read out-of-bounds memory from the entries[] array?

[ ... ]
> @@ -3434,6 +3591,12 @@ PyMODINIT_FUNC PyInit_perf(void)
> Py_INCREF(&pyrf_session__type);
> PyModule_AddObject(module, "session", (PyObject *)&pyrf_session__type);
>
> + Py_INCREF(&pyrf_branch_entry__type);
> + PyModule_AddObject(module, "branch_entry", (PyObject *)&pyrf_branch_entry__type);

Does this code leak the type object if PyModule_AddObject() fails?

According to the Python C-API, PyModule_AddObject() only steals the reference
on success. If it returns -1, the reference is not stolen.

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