From 08915b9f1837de77bda150abc43e6e26e72175e1 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 21 Jul 2026 16:13:46 +0200 Subject: [PATCH] binfmt_misc: normalize the per-exec invocation flags A static entry fixes its invocation flags at registration. A 'B' entry's load program picks them per exec. Since load_misc_binary() branches on which kind of entry matched and then applies the two flag sets side by side every flag is handled twice and each new one has to be added to both arms. Translate the 'B' flags into the entry flags they mirror and let the dispatch act on a single set of flags. The boolean the two arms communicated 'P' can be removed. No functional change. Link: https://patch.msgid.link/20260721-work-bpf-binfmt_misc-ptinterp-v2-4-e57866e4ae0f@kernel.org Reviewed-by: Farid Zakaria Signed-off-by: Christian Brauner (Amutable) --- fs/binfmt_misc.c | 62 ++++++++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index d568cd5cc928..e87da5ece641 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -319,6 +319,40 @@ static const char *entry_select_interpreter(const struct binfmt_misc_entry *e, return ERR_PTR(retval); } +/** + * entry_invocation_flags - the invocation flags in effect for this exec + * @e: matched binary type handler + * @bprm: binary that is being executed + * + * A static entry fixes its flags at registration, a 'B' entry's load program + * picks them per exec with bpf_binprm_set_flags(). Translate the latter into + * the former, implications included, so the dispatch has one set to act on. + * + * Return: the invocation flags for this exec + */ +static unsigned long entry_invocation_flags(const struct binfmt_misc_entry *e, + struct linux_binprm *bprm) +{ + unsigned long flags = 0; + u64 bpf_flags; + + if (!test_bit(MISC_FMT_BPF_BIT, &e->flags)) + return e->flags; + + bpf_flags = bprm->bpf_flags; + /* Clear so they can't accumulate into a nested interpreter level. */ + bprm->bpf_flags = 0; + + if (bpf_flags & BPF_BINPRM_PRESERVE_ARGV0) + flags |= MISC_FMT_PRESERVE_ARGV0; + if (bpf_flags & BPF_BINPRM_EXECFD) + flags |= MISC_FMT_OPEN_BINARY; + if (bpf_flags & BPF_BINPRM_CREDENTIALS) + flags |= MISC_FMT_CREDENTIALS | MISC_FMT_OPEN_BINARY; + + return flags; +} + /* * the loader itself */ @@ -328,7 +362,7 @@ static int load_misc_binary(struct linux_binprm *bprm) const char *interpreter; struct file *interp_file; struct binfmt_misc *misc; - bool preserve_argv0, want_execfd, want_creds; + unsigned long flags; int retval; misc = current_binfmt_misc(); @@ -347,28 +381,10 @@ static int load_misc_binary(struct linux_binprm *bprm) if (IS_ERR(interpreter)) return PTR_ERR(interpreter); - /* - * The invocation flags are fixed at registration for a static handler - * and chosen per exec by the load program, via bpf_binprm_set_flags(), - * for a bpf one. - */ - if (test_bit(MISC_FMT_BPF_BIT, &fmt->flags)) { - u64 f = bprm->bpf_flags; - - /* Clear so it can't accumulate into a nested interpreter level. */ - bprm->bpf_flags = 0; - - preserve_argv0 = f & BPF_BINPRM_PRESERVE_ARGV0; - want_creds = f & BPF_BINPRM_CREDENTIALS; - want_execfd = f & (BPF_BINPRM_CREDENTIALS | BPF_BINPRM_EXECFD); - } else { - preserve_argv0 = fmt->flags & MISC_FMT_PRESERVE_ARGV0; - want_creds = fmt->flags & MISC_FMT_CREDENTIALS; - want_execfd = fmt->flags & MISC_FMT_OPEN_BINARY; - } + flags = entry_invocation_flags(fmt, bprm); /* The entry's own choice - not one accumulated from an earlier level. */ - if (preserve_argv0) { + if (flags & MISC_FMT_PRESERVE_ARGV0) { bprm->interp_flags |= BINPRM_FLAGS_PRESERVE_ARGV0; } else { retval = remove_arg_zero(bprm); @@ -424,9 +440,9 @@ static int load_misc_binary(struct linux_binprm *bprm) return PTR_ERR(interp_file); bprm->interpreter = interp_file; - if (want_execfd) + if (flags & MISC_FMT_OPEN_BINARY) bprm->have_execfd = 1; - if (want_creds) + if (flags & MISC_FMT_CREDENTIALS) bprm->execfd_creds = 1; return 0; }