Re: [PATCH v5 30/58] perf flamegraph: Port flamegraph to use python module

sashiko-bot
2026-04-24T17:22:43Z

Sashiko AI review found 3 potential issue(s):
- [High] Event name filtering drops all events due to incorrect string comparison.
- [High] Unresolved symbols evaluate to `None`, producing invalid JSON that breaks the flamegraph renderer.
- [High] Printing unverified template content via `input()` enables terminal escape injection attacks and corrupts output streams.
--

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

perf flamegraph: Port flamegraph to use python module

This commit adds a port of the flamegraph script that uses the perf
python module directly. This approach improves performance by avoiding
intermediate dictionaries for event fields.

> diff --git a/tools/perf/python/flamegraph.py b/tools/perf/python/flamegraph.py
> new file mode 100755
> index 00000000000000..f3f69e5a88c249
> --- /dev/null
> +++ b/tools/perf/python/flamegraph.py
[ ... ]
> + def process_event(self, sample) -> None:
> + """Process a single perf sample event."""
> + if self.args.event_name and str(sample.evsel) != self.args.event_name:
> + return

Does this comparison drop all filtered events? The Python extension's
implementation of str(evsel) returns a string formatted with parentheses,
such as "evsel(cycles)".

Comparing "evsel(cycles)" to the provided event name like "cycles" will
always evaluate to false, causing the script to silently drop events and
produce an empty flamegraph.

[ ... ]
> + else:
> + # Fallback if no callchain
> + name = getattr(sample, "symbol", "[unknown]")
> + libtype = self.get_libtype_from_dso(getattr(sample, "dso", "[unknown]"))
> + node = self.find_or_create_node(node, name, libtype)

Will getattr() return None here when a symbol cannot be resolved? The C Python
extension explicitly returns Py_RETURN_NONE for unresolved symbols.

Since the symbol attribute exists but evaluates to None without raising an
AttributeError, getattr() will return None instead of the "[unknown]" fallback.

This None value propagates to the node initialization and serializes as null
in the JSON output. The d3-flame-graph frontend expects the node name to be a
string and throws a TypeError, breaking the UI.

[ ... ]
> + elif template_md5sum:
> + assert output_str is not None
> + download_md5sum = hashlib.md5(output_str.encode("utf-8")).hexdigest()
> + if download_md5sum != template_md5sum:
> + s = None
> + while s not in ["y", "n"]:
> + s = input(f"""Unexpected template md5sum.
> +{download_md5sum} != {template_md5sum}, for:
> +{output_str}
> +continue?[yn] """).lower()

Does printing output_str here enable terminal escape injection attacks? When
the downloaded template fails its integrity check, the unverified payload is
embedded directly into the prompt.

Because input() inherently writes its prompt string directly to the terminal,
any malicious ANSI escape sequences in a compromised payload will execute
before the user can even answer the prompt.

Additionally, if the user redirects output to stdout using "-o -", does this
write the interactive prompt directly into the output payload while hanging
the script indefinitely waiting for stdin?

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