Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Christian Brauner <brauner@kernel.org>
To: Farid Zakaria <farid.m.zakaria@gmail.com>,
	 linux-fsdevel@vger.kernel.org
Cc: Daniel Borkmann <daniel@iogearbox.net>,
	 Alexei Starovoitov <ast@kernel.org>, Kees Cook <kees@kernel.org>,
	 Alexander Viro <viro@zeniv.linux.org.uk>,
	Jan Kara <jack@suse.cz>,  Jonathan Corbet <corbet@lwn.net>,
	linux-mm@kvack.org, bpf@vger.kernel.org,  jannh@google.com,
	mail@johnericson.me,
	 "Christian Brauner (Amutable)" <brauner@kernel.org>
Subject: [PATCH 19/21] binfmt_misc: let a bpf handler request loader substitution
Date: Mon, 20 Jul 2026 11:33:42 +0200	[thread overview]
Message-ID: <20260720-work-bpf-binfmt_misc-ptinterp-v1-19-ddb76c9a508e@kernel.org> (raw)
In-Reply-To: <20260720-work-bpf-binfmt_misc-ptinterp-v1-0-ddb76c9a508e@kernel.org>

Give bpf handlers the per-exec equivalent of the static 'L' flag. A
load program that sets BPF_BINPRM_LOADER has its selected interpreter
substituted for the binary's PT_INTERP instead of run with the binary
as payload. The binary otherwise executes as a fully native exec.

A single handler can now grade its dispatch per binary: native-arch ELF
with PT_INTERP gets loader substitution for full native identity.
Anything else, such as foreign arch, static, non-ELF can use transparent
or classic dispatch. The load program can read the binary's ELF header
from bprm->buf to make that call.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 Documentation/admin-guide/binfmt-misc.rst |  3 +++
 fs/binfmt_misc.c                          |  2 ++
 fs/binfmt_misc_bpf.c                      | 13 ++++++++++---
 include/linux/binfmt_misc.h               |  4 ++++
 4 files changed, 19 insertions(+), 3 deletions(-)

diff --git a/Documentation/admin-guide/binfmt-misc.rst b/Documentation/admin-guide/binfmt-misc.rst
index 0946ca1923f2..8c861bf0c56c 100644
--- a/Documentation/admin-guide/binfmt-misc.rst
+++ b/Documentation/admin-guide/binfmt-misc.rst
@@ -208,6 +208,9 @@ decide them differently for each binary it handles:
   run a binary passed as an inaccessible ``O_CLOEXEC`` file descriptor to
   ``execveat()``, which a path-splicing dispatch cannot: the interpreter
   has no path by which to open it.
+- ``BPF_BINPRM_LOADER`` substitutes the interpreter for the binary's
+  ``PT_INTERP`` and runs the binary as a fully native exec (the ``L``
+  flag). It excludes the other flags and a staged interpreter argument.
 
 Because these are program choices, a ``B`` entry carries no flags in the
 register string; ``F`` (pre-open a fixed interpreter) has no meaning for it.
diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
index ee48dab2638a..855b41016cf0 100644
--- a/fs/binfmt_misc.c
+++ b/fs/binfmt_misc.c
@@ -354,6 +354,8 @@ static unsigned long entry_invocation_flags(const struct binfmt_misc_entry *e,
 		flags |= MISC_FMT_CREDENTIALS | MISC_FMT_OPEN_BINARY;
 	if (bpf_flags & BPF_BINPRM_TRANSPARENT)
 		flags |= MISC_FMT_TRANSPARENT | MISC_FMT_OPEN_BINARY;
+	if (bpf_flags & BPF_BINPRM_LOADER)
+		flags |= MISC_FMT_LOADER;
 
 	return flags;
 }
diff --git a/fs/binfmt_misc_bpf.c b/fs/binfmt_misc_bpf.c
index d06cacc5c8c7..105c8afbb3f2 100644
--- a/fs/binfmt_misc_bpf.c
+++ b/fs/binfmt_misc_bpf.c
@@ -179,8 +179,10 @@ __bpf_kfunc int bpf_binprm_set_interp_arg(struct linux_binprm *bprm,
  * BPF_BINPRM_CREDENTIALS computes credentials from the binary, and
  * BPF_BINPRM_EXECFD hands the binary to the interpreter through AT_EXECFD.
  * BPF_BINPRM_TRANSPARENT additionally leaves the argument vector untouched,
- * making the exec look like a direct execution of the binary. Calling it
- * again replaces the flags, passing zero clears them again.
+ * making the exec look like a direct execution of the binary.
+ * BPF_BINPRM_LOADER substitutes the interpreter for the binary's PT_INTERP
+ * and runs the binary as a native exec; it excludes every other flag.
+ * Calling it again replaces the flags, passing zero clears them again.
  *
  * Return: 0 on success, -EINVAL if @flags contains an unknown bit or an
  * invalid combination
@@ -189,7 +191,12 @@ __bpf_kfunc int bpf_binprm_set_flags(struct linux_binprm *bprm,
 				     enum bpf_binprm_flags flags)
 {
 	if (flags & ~(BPF_BINPRM_PRESERVE_ARGV0 | BPF_BINPRM_CREDENTIALS |
-		      BPF_BINPRM_EXECFD | BPF_BINPRM_TRANSPARENT))
+		      BPF_BINPRM_EXECFD | BPF_BINPRM_TRANSPARENT |
+		      BPF_BINPRM_LOADER))
+		return -EINVAL;
+
+	/* Loader substitution is a native exec: no splice, execfd or creds work. */
+	if ((flags & BPF_BINPRM_LOADER) && (flags & ~BPF_BINPRM_LOADER))
 		return -EINVAL;
 
 	/* Transparency preserves the whole argv, argv[0] included. */
diff --git a/include/linux/binfmt_misc.h b/include/linux/binfmt_misc.h
index 26da749391b4..4abdfd36b3fa 100644
--- a/include/linux/binfmt_misc.h
+++ b/include/linux/binfmt_misc.h
@@ -19,6 +19,9 @@ struct user_namespace;
  * @BPF_BINPRM_TRANSPARENT: leave argv untouched, the interpreter takes the
  *                          binary from AT_EXECFD (like the 'T' flag); implies
  *                          execfd, excludes preserve-argv0
+ * @BPF_BINPRM_LOADER: substitute the interpreter for the binary's PT_INTERP
+ *                     and run the binary as a native exec (like the 'L'
+ *                     flag); excludes every other flag
  *
  * Set from a load program with bpf_binprm_set_flags(). Unlike a static entry,
  * a bpf handler chooses these per exec rather than once at registration.
@@ -28,6 +31,7 @@ enum bpf_binprm_flags {
 	BPF_BINPRM_CREDENTIALS		= (1ULL << 1),
 	BPF_BINPRM_EXECFD		= (1ULL << 2),
 	BPF_BINPRM_TRANSPARENT		= (1ULL << 3),
+	BPF_BINPRM_LOADER		= (1ULL << 4),
 };
 
 /**

-- 
2.53.0



  parent reply	other threads:[~2026-07-20  9:35 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-20  9:33 [PATCH 00/21] binfmt_misc: transparent interpreters and PT_INTERP loader substitution Christian Brauner
2026-07-20  9:33 ` [PATCH 01/21] exec: do not act on a stale execfd request without an executable Christian Brauner
2026-07-20  9:33 ` [PATCH 02/21] docs, binfmt_misc: keep general usage out of the handler sections Christian Brauner
2026-07-20  9:33 ` [PATCH 03/21] binfmt_misc: table-drive the register string flags Christian Brauner
2026-07-20  9:33 ` [PATCH 04/21] binfmt_misc: normalize the per-exec invocation flags Christian Brauner
2026-07-20  9:33 ` [PATCH 05/21] binfmt_misc: split out entry_open_interpreter() Christian Brauner
2026-07-20  9:33 ` [PATCH 06/21] binfmt_misc: split out build_interp_argv() Christian Brauner
2026-07-20  9:33 ` [PATCH 07/21] exec: release the replaced file with do_close_execat() Christian Brauner
2026-07-20  9:33 ` [PATCH 08/21] exec: add AT_FLAGS_TRANSPARENT_INTERP Christian Brauner
2026-07-20  9:33 ` [PATCH 09/21] exec: label mm->exe_file with the binary for a transparent dispatch Christian Brauner
2026-07-20  9:33 ` [PATCH 10/21] binfmt_misc: add transparent interpreter dispatch Christian Brauner
2026-07-20  9:33 ` [PATCH 11/21] binfmt_misc: add a static transparent flag 'T' Christian Brauner
2026-07-20  9:33 ` [PATCH 12/21] binfmt_misc: let a bpf handler run the interpreter transparently Christian Brauner
2026-07-20  9:33 ` [PATCH 13/21] selftests/exec: convert the binfmt_misc bpf test to the kselftest harness Christian Brauner
2026-07-20  9:33 ` [PATCH 14/21] selftests/exec: test the transparent binfmt_misc mode Christian Brauner
2026-07-20  9:33 ` [PATCH 15/21] binfmt_misc: document the transparent identity contract Christian Brauner
2026-07-20  9:33 ` [PATCH 16/21] exec: carry a PT_INTERP substitute in struct linux_binprm Christian Brauner
2026-07-20  9:33 ` [PATCH 17/21] binfmt_elf: consume a stashed PT_INTERP substitute Christian Brauner
2026-07-20  9:33 ` [PATCH 18/21] binfmt_misc: add the 'L' loader substitution flag Christian Brauner
2026-07-20  9:33 ` Christian Brauner [this message]
2026-07-20  9:33 ` [PATCH 20/21] selftests/exec: test binfmt_misc loader substitution Christian Brauner
2026-07-20  9:33 ` [PATCH 21/21] binfmt_misc: document " Christian Brauner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260720-work-bpf-binfmt_misc-ptinterp-v1-19-ddb76c9a508e@kernel.org \
    --to=brauner@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=corbet@lwn.net \
    --cc=daniel@iogearbox.net \
    --cc=farid.m.zakaria@gmail.com \
    --cc=jack@suse.cz \
    --cc=jannh@google.com \
    --cc=kees@kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=mail@johnericson.me \
    --cc=viro@zeniv.linux.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox