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.
Link: https://patch.msgid.link/20260721-work-bpf-binfmt_misc-ptinterp-v2-19-e57866e4ae0f@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
``#!`` path and needs to preserve the argument that followed it.
The invocation flags a static entry fixes at registration - ``P``, ``C``,
-``O`` and ``T`` - are per-exec choices for a bpf handler, made by the ``load``
-program with the ``bpf_binprm_set_flags()`` kfunc, so a single handler can
-decide them differently for each binary it handles:
+``O``, ``T`` and ``L`` - are per-exec choices for a bpf handler, made by the
+``load`` program with the ``bpf_binprm_set_flags()`` kfunc, so a single
+handler can decide them differently for each binary it handles:
- ``BPF_BINPRM_PRESERVE_ARGV0`` keeps the caller's ``argv[0]`` (the ``P``
flag).
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.
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;
}
* @flags: an OR of enum bpf_binprm_flags values
*
* To be called from the load program of a struct binfmt_misc_ops handler. It
- * decides per exec what a static entry fixes at registration with the P, C, O
- * and T flags: BPF_BINPRM_PRESERVE_ARGV0 keeps the caller's argv[0],
+ * decides per exec what a static entry fixes at registration with the P, C,
+ * O, T and L flags: BPF_BINPRM_PRESERVE_ARGV0 keeps the caller's argv[0],
* 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
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. */
* @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.
BPF_BINPRM_CREDENTIALS = (1ULL << 1),
BPF_BINPRM_EXECFD = (1ULL << 2),
BPF_BINPRM_TRANSPARENT = (1ULL << 3),
+ BPF_BINPRM_LOADER = (1ULL << 4),
};
/**