Re: [PATCH v1 2/2] perf/core: Clamp deferred callchain entry count to fit u16 header.size
From: Ian Rogers
Date: Tue Nov 03 2026 - 23:28:31 EST
On Sun, Oct 25, 2026 at 2:55 PM Ian Rogers <irogers@xxxxxxxxxx> wrote:
>
> In perf_callchain_deferred_output(), if trace->nr is large enough that
> sizeof(deferred_event->event) + trace->nr * sizeof(u64) + id_header_size
> exceeds U16_MAX, header.size wraps while the output loop still writes
> all trace->nr entries into the ring buffer. Clamp nr per event (accounting
> for event->id_header_size when sample_id_all is enabled) so the record
> never overflows u16 header.size.
>
> Fixes: c69993ecdd4d ("perf: Support deferred user unwind")
> Signed-off-by: Ian Rogers <irogers@xxxxxxxxxx>
Ping. +Steve and Masami
Thanks,
Ian
> ---
> kernel/events/core.c | 16 +++++++++++-----
> 1 file changed, 11 insertions(+), 5 deletions(-)
>
> diff --git a/kernel/events/core.c b/kernel/events/core.c
> index b43883a42566..37824ca7fe97 100644
> --- a/kernel/events/core.c
> +++ b/kernel/events/core.c
> @@ -10385,8 +10385,6 @@ struct perf_callchain_deferred_event {
> struct {
> struct perf_event_header header;
> u64 cookie;
> - u64 nr;
> - u64 ips[];
> } event;
> };
>
> @@ -10395,12 +10393,19 @@ static void perf_callchain_deferred_output(struct perf_event *event, void *data)
> struct perf_callchain_deferred_event *deferred_event = data;
> struct perf_output_handle handle;
> struct perf_sample_data sample;
> + u64 nr = deferred_event->trace->nr;
> + u16 size, id_size, max_payload;
> int ret;
> - u16 size = sizeof(deferred_event->event) + (deferred_event->trace->nr * sizeof(u64));
>
> if (!event->attr.defer_output)
> return;
>
> + id_size = event->attr.sample_id_all ? event->id_header_size : 0;
> + max_payload = round_down(U16_MAX - sizeof(deferred_event->event) -
> + sizeof(u64) - id_size, sizeof(u64));
> + nr = min_t(u64, nr, max_payload / sizeof(u64));
> + size = sizeof(deferred_event->event) + sizeof(u64) + nr * sizeof(u64);
> +
> /* XXX do we really need sample_id_all for this ??? */
> perf_event_header__init(&deferred_event->event.header, &sample,
> PERF_RECORD_CALLCHAIN_DEFERRED,
> @@ -10414,8 +10419,10 @@ static void perf_callchain_deferred_output(struct perf_event *event, void *data)
> return;
>
> perf_output_put(&handle, deferred_event->event);
> - for (int i = 0; i < deferred_event->trace->nr; i++) {
> + perf_output_put(&handle, nr);
> + for (u64 i = 0; i < nr; i++) {
> u64 entry = deferred_event->trace->entries[i];
> +
> perf_output_put(&handle, entry);
> }
> perf_event__output_id_sample(event, &handle, &sample);
> @@ -10430,7 +10437,6 @@ static void perf_unwind_deferred_callback(struct unwind_work *work,
> .trace = trace,
> .event = {
> .cookie = cookie,
> - .nr = trace->nr,
> },
> };
>
> --
> 2.56.0.307.g5fe7bd680c-goog
>