From: Eneshan Erdoğan Karaca <[email protected]> A binfmt_misc entry can select an interpreter for an exec. With a BPF 'B' handler, that selection can be made by a BPF struct_ops program. The kernel commits the decision in bprm->interp and bprm->flags, but it does not emit an event at the selection point.
Existing observers have to reconstruct the decision from /proc/PID/exe, AT_EXECFD, AT_FLAGS, or /proc/PID/maps. These sources are mode-dependent: a transparent dispatch still reports the original binary in /proc/PID/exe, and a short-lived process can exit before the observer reads it. A classic dispatch reports the interpreter, but not which handler selected it or which invocation flags were used. Add a tracepoint after the interpreter and the invocation flags are selected. The caller passes the matched entry and the raw flags; the trace event resolves the handler name and masks the flags with MISC_FMT_INVOCATION_FLAGS. The event reports the binary filename, the selected interpreter, the handler name, the invocation flags, and the pid. The tracepoint is observability only; it does not change exec behavior. Assisted-by: LLM Signed-off-by: Eneshan Erdoğan Karaca <[email protected]> --- v2: - Pass the matched entry and the raw flags to the tracepoint. - Resolve the handler name and mask the flags in the trace event. Thanks to Steven Rostedt for the review and the claws-mail tip. The PoC and A/B results are available on request. fs/binfmt_misc.c | 9 +++++ include/trace/events/binfmt_misc.h | 55 ++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 include/trace/events/binfmt_misc.h diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index 620da85..58a8283 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -118,6 +118,13 @@ struct binfmt_misc_entry { char buf[]; /* register string, fields point in here */ }; +/* + * The trace event needs struct binfmt_misc_entry and the MISC_FMT_* + * flags, so include it after they are defined. + */ +#define CREATE_TRACE_POINTS +#include <trace/events/binfmt_misc.h> + /* * Max length of the register string. Determined by: * - 7 delimiters @@ -644,6 +651,8 @@ static int load_misc_binary(struct linux_binprm *bprm) flags = entry_invocation_flags(fmt, bprm); + trace_binfmt_misc_select(bprm, fmt, interpreter, flags); + /* No argv is built for a staged argument to land in. */ if ((flags & (MISC_FMT_LOADER | MISC_FMT_TRANSPARENT)) && bprm->bpf_interp_arg) diff --git a/include/trace/events/binfmt_misc.h b/include/trace/events/binfmt_misc.h new file mode 100644 index 0000000..61a0de0 --- /dev/null +++ b/include/trace/events/binfmt_misc.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Tracepoint for binfmt_misc binary type handler dispatch. + * + * Fires once for every exec a binfmt_misc entry matches, after the + * interpreter and the invocation flags have been selected. Unlike + * sched_process_exec (which reports bprm->filename) and /proc/PID/exe + * (which a transparent dispatch sets to the binary), this reports the + * interpreter that actually runs, so observers can see through a + * transparent dispatch. + */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM binfmt_misc + +#if !defined(_TRACE_BINFMT_MISC_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_BINFMT_MISC_H + +#include <linux/tracepoint.h> +#include <linux/binfmts.h> +#include <linux/binfmt_misc.h> +#include <linux/bitops.h> + +TRACE_EVENT(binfmt_misc_select, + + TP_PROTO(struct linux_binprm *bprm, const struct binfmt_misc_entry *fmt, + const char *interp, unsigned long flags), + + TP_ARGS(bprm, fmt, interp, flags), + + TP_STRUCT__entry( + __string(filename, bprm->filename) + __string(interp, interp) + __string(handler, test_bit(MISC_FMT_BPF_BIT, &fmt->flags) ? + fmt->bpf_ops->name : "") + __field(unsigned long, flags) + __field(pid_t, pid) + ), + + TP_fast_assign( + __assign_str(filename); + __assign_str(interp); + __assign_str(handler); + __entry->flags = flags & MISC_FMT_INVOCATION_FLAGS; + __entry->pid = current->pid; + ), + + TP_printk("pid=%d filename=%s interp=%s handler=%s flags=%#lx", + __entry->pid, __get_str(filename), __get_str(interp), + __get_str(handler), __entry->flags) +); + +#endif /* _TRACE_BINFMT_MISC_H */ + +/* This part must be outside protection */ +#include <trace/define_trace.h> -- 2.53.0
