``AT_FLAGS_TRANSPARENT_INTERP`` contract. Combining ``T``
with ``P`` is rejected: transparency preserves the whole
argument vector, argv[0] included.
+ ``L`` - loader substitution
+ Do not run the interpreter on the binary at all: load the
+ binary itself as a fully native exec and substitute the
+ interpreter for the loader named in the binary's
+ ``PT_INTERP``. See the "Loader substitution" section
+ below. ``L`` rejects ``T``, ``P``, ``O`` and ``C``;
+ ``F`` composes.
There are some restrictions:
- the magic must reside in the first 128 bytes of the file, i.e.
offset+size(magic) has to be less than 128
- the interpreter string may not exceed 127 characters
- - an interpreter used with ``C`` but without ``F`` has to be named by an
- absolute path. It is opened when the binary is executed, so a relative
- one would be resolved against the working directory of whoever runs
- the binary
+ - an interpreter used with ``C`` or ``L`` but without ``F`` has to be
+ named by an absolute path. It is opened when the binary is executed, so
+ a relative one would be resolved against the working directory of
+ whoever runs the binary
To use binfmt_misc you have to mount it first. You can mount it with
MISC_FMT_CREDENTIALS = (1U << 29),
MISC_FMT_OPEN_FILE = (1U << 28),
MISC_FMT_TRANSPARENT = (1U << 27),
+ MISC_FMT_LOADER = (1U << 26),
};
/**
{ 'C', MISC_FMT_CREDENTIALS, MISC_FMT_OPEN_BINARY, "credentials from the binary" },
{ 'F', MISC_FMT_OPEN_FILE, 0, "open interpreter file now" },
{ 'T', MISC_FMT_TRANSPARENT, MISC_FMT_OPEN_BINARY, "transparent" },
+ { 'L', MISC_FMT_LOADER, 0, "loader substitution" },
};
/* Look up a flag character, NULL if @c is not one. */
unsigned long flags;
int retval;
+ /* Only binfmt_misc stages one and exec_binprm() clears it per round. */
+ WARN_ON_ONCE(bprm->loader);
+
misc = current_binfmt_misc();
if (!READ_ONCE(misc->enabled))
return -ENOEXEC;
flags = entry_invocation_flags(fmt, bprm);
/* No argv is built for a staged argument to land in. */
- if ((flags & MISC_FMT_TRANSPARENT) && bprm->bpf_interp_arg)
+ if ((flags & (MISC_FMT_LOADER | MISC_FMT_TRANSPARENT)) &&
+ bprm->bpf_interp_arg)
return -EINVAL;
+ /*
+ * Stash the interpreter for binfmt_elf to consume in place of the
+ * binary's PT_INTERP and decline the match, so the search continues
+ * to the real format in the same round.
+ */
+ if (flags & MISC_FMT_LOADER) {
+ interp_file = entry_open_interpreter(fmt, interpreter);
+ if (IS_ERR(interp_file)) {
+ retval = PTR_ERR(interp_file);
+ /* Declining here would run the binary's own PT_INTERP. */
+ return retval == -ENOEXEC ? -EACCES : retval;
+ }
+
+ bprm->loader = interp_file;
+ return -ENOEXEC;
+ }
+
if (!(flags & MISC_FMT_TRANSPARENT)) {
retval = build_interp_argv(bprm, interpreter, flags);
if (retval)
(e->flags & MISC_FMT_PRESERVE_ARGV0))
return ERR_PTR(-EINVAL);
+ /* A native exec splices no argv, passes no execfd and needs no creds. */
+ if ((e->flags & MISC_FMT_LOADER) &&
+ (e->flags & (MISC_FMT_TRANSPARENT | MISC_FMT_PRESERVE_ARGV0 |
+ MISC_FMT_CREDENTIALS | MISC_FMT_OPEN_BINARY)))
+ return ERR_PTR(-EINVAL);
+
if (*p == '\n')
p++;
if (p != buf + count)
return ERR_PTR(-EINVAL);
/* Non-F opens the interp at exec against the caller's cwd; require absolute. */
- if ((e->flags & MISC_FMT_CREDENTIALS) &&
+ if ((e->flags & (MISC_FMT_LOADER | MISC_FMT_CREDENTIALS)) &&
!(e->flags & MISC_FMT_OPEN_FILE) &&
e->interpreter[0] != '/')
return ERR_PTR(-EINVAL);