Re: [PATCH v4 00/18] perf trace: Fix BPF filtering and make tracing tests non-exclusive

From: Ian Rogers

Date: Tue Sep 22 2026 - 18:43:59 EST


On Tue, Sep 22, 2026 at 6:11 AM Arnaldo Carvalho de Melo
<acme@xxxxxxxxxx> wrote:
> Lots of changes, from a quick look all look sensible, I'll test it some
> more, as in one case it segfaulted:
>
> root@x2:~# strace -e perf_event_open perf trace -a sleep 1
> <SNIP>
> 262.297 ( 0.002 ms): ptyxis/30654 write(fd: 4<anon_inode:[eventfd]>, buf: \1\0\0\0\0\0\0\0, count: 8) = 8
> 262.304 ( 0.011 ms): ptyxis/30654 ppoll(ufds: 0x55dfa7a62f10, nfds: 11, tsp: 0x7ffc0701eed0, sigsetsize: 8) = 2
> 262.318 ( 0.002 ms): ptyxis/30654 read(fd: 4<anon_inode:[eventfd]>, buf: 0x7ffc0701ee10, count: 8) = 8
> 262.324 ( 0.002 ms): ptyxis/30654 write(fd: 4<anon_inode:[eventfd]>, buf: \1\0\0\0\0\0\0\0, count: 8) = 8
> 262.328 ( 0.002 ms): ptyxis/30654 read(fd: 31</dev/ptmx>, buf: 0x55dfa8a54f19, count: 5751) = 8
> 262.332 ( 0.002 ms): ptyxis/30654 read(fd: 31</dev/ptmx>, buf: 0x55dfa8a54f20, count: 5744) = -1 (unknown) (Resource temporarily unavailable)
> LOST 70 events!
> --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x117f000} ---
> perf: Segmentation fault
> #0 0x73104f in dump_stack debug.c:366
> #1 0x7310c5 in sighandler_dump_stack debug.c:378
> #2 0x7f749ea66fb0 in __restore_rt libc.so.6[19fb0]
> #3 0x4c0fa0 in syscall_arg__scnprintf_buf builtin-trace.c:1955
> #4 0x4c2f3d in syscall_arg_fmt__scnprintf_val builtin-trace.c:2632
> #5 0x4c33ae in syscall__scnprintf_args builtin-trace.c:2722
> #6 0x4c43d3 in trace__sys_enter builtin-trace.c:3094
> #7 0x4c7865 in trace__handle_event builtin-trace.c:4013
> #8 0x4c9ab4 in __trace__deliver_event builtin-trace.c:4962
> #9 0x4c9c06 in trace__deliver_event builtin-trace.c:4988
> #10 0x4caed8 in trace__run builtin-trace.c:5397
> #11 0x4cf530 in cmd_trace builtin-trace.c:6681
> #12 0x4da43c in run_builtin perf.c:380
> #13 0x4da68e in handle_internal_command perf.c:430
> #14 0x4da7ea in run_argv perf.c:477
> #15 0x4daabe in main perf.c:577
> #16 0x7f749ea50681 in __libc_start_call_main libc.so.6[3681]
> #17 0x7f749ea50798 in __libc_start_main@@GLIBC_2.34 libc.so.6[3798]
> #18 0x409f25 in _start perf[409f25]
> --- SIGSEGV {si_signo=SIGSEGV, si_code=SI_TKILL, si_pid=1097958, si_uid=0} ---
> +++ killed by SIGSEGV (core dumped) +++
> Segmentation fault (core dumped) strace -e perf_event_open perf trace -a sleep 1
> root@x2:~#
>
> root@x2:~# uname -a
> Linux x2 7.2.5-200.fc44.x86_64 #1 SMP PREEMPT_DYNAMIC Fri Sep 11 15:11:05 UTC 2026 x86_64 GNU/Linux
> root@x2:~#

Thanks Arnaldo for testing, and sorry for the crash. Working with
antigravity I got the analysis below for the crash and I'll fix it in
v5.

I can account for it, and it is a regression from patch 7 ("perf trace:
Filter events in BPF and avoid tracepoint vetoes") landing on a missing
bounds check that predates the series.

Before the series syscall_unaugmented() was:

SEC("tp/raw_syscalls/sys_enter")
int syscall_unaugmented(struct syscall_enter_args *args)
{
return 1;
}

so an unaugmented sys_enter emitted nothing at all. Patch 7 moved it to
tp/syscalls/sys_enter_unaugmented and made it output the 64 byte struct
syscall_enter_args to __augmented_syscalls__, so that perf trace could
stop listening on the raw tracepoint and stop vetoing it.

That 64 byte record does not come back as 64 bytes.
perf_sample_save_raw_data() does:

size = round_up(sum + sizeof(u32), sizeof(u64));
raw->size = size - sizeof(u32);
frag->pad = raw->size - sum;

so sum of 64 gives raw->size of 68, and the 4 bytes of padding are
written by:

__output_skip(handle, NULL, frag->pad);

which advances over them rather than zeroing them. They are whatever the
ring buffer last held there.

Userspace then does:

*augmented_args_size = sample->raw_size - args_size;

with args_size of 64, gets 4, decides that is an augmented payload, and
copies those 4 stale bytes into argbuf as the size and int_arg of a
struct augmented_arg. syscall_arg__scnprintf_buf() has no bound on the
length it reads there:

for (int j = 0; j < augmented_arg->size; ++j)

so a size taken from, say, the low half of a pointer walks straight off
the end of the 8K argbuf in bss. SCA_BUF is used by exactly one syscall,
write()'s buf argument, which is why a terminal emulator's writes were
what tripped it. The si_addr in your report is page aligned just past
that buffer, which fits.

btf_struct_scnprintf() already had the check that would have caught it,
which is why the BTF path survived this and SCA_BUF did not: a 4 byte
trailing run is smaller than a struct augmented_arg, so it returns early.

v5 fixes it in two patches, placed before the patch that introduces the
unaugmented output so that no commit in the series is broken:

perf trace: Bounds check augmented arguments before reading them

Lifts btf_struct_scnprintf()'s check into a shared
syscall_arg__augmented_args_valid() in beauty.h and uses it at every
site that reads an augmented argument. Besides the crash site this
covers the timespec, sockaddr and perf_event_attr beautifiers, which
read a fixed sized type out of a payload that may be shorter than
it. Two unbounded reads that are not about the header are fixed with
them: perf_event_attr__fprintf() always reads a whole struct
perf_event_attr, so the payload is now copied into a zero padded
local rather than read in place, and the AF_LOCAL path is printed
with a length from the payload rather than as a string it need not
be.

perf trace: Do not read sample padding as an augmented argument

A trailing run shorter than a struct augmented_arg cannot be one, so
it is recognised as the padding it is rather than being offered to
the beautifiers at all.

The first alone stops the SIGSEGV. The second stops the syscall being
printed with a bogus argument in the cases where the padding happens to
hold a length that passes validation.

Thanks,
Ian