interpreter is opened with the credentials of the task doing the exec,
exactly as a statically registered interpreter would be.
+The ``load`` program can also pass a single argument to the interpreter with
+the ``bpf_binprm_set_interp_arg()`` kfunc. It is inserted between the
+interpreter and the binary, exactly like the optional argument of a ``#!``
+interpreter line, e.g. for a handler that resolves ``$ORIGIN`` in a script's
+``#!`` path and needs to preserve the argument that followed it.
+
A handler is looked up only in the user namespace the struct_ops map was
registered in. Handlers are not inherited, so an entry can only reference a
handler registered in the same user namespace as its binfmt_misc instance.
if (!test_bit(MISC_FMT_BPF_BIT, &e->flags))
return e->interpreter;
- /* Drop any interpreter a previous chain level staged. */
+ /* Drop any interpreter or flags a previous chain level staged. */
kfree(bprm->bpf_interp);
bprm->bpf_interp = NULL;
+ bprm->bpf_flags = 0;
retval = e->bpf_ops->load(bprm);
if (retval) {
/* Keep a program-supplied error within errno range. */
if (retval > 0 || retval < -MAX_ERRNO)
retval = -ENOEXEC;
- return ERR_PTR(retval);
+ goto drop_staged;
}
/* Selecting an interpreter is part of the contract. */
- if (!bprm->bpf_interp)
- return ERR_PTR(-ENOEXEC);
+ if (!bprm->bpf_interp) {
+ retval = -ENOEXEC;
+ goto drop_staged;
+ }
return bprm->bpf_interp;
+
+drop_staged:
+ /* A failing load leaves nothing behind for later entries. */
+ kfree(bprm->bpf_interp_arg);
+ bprm->bpf_interp_arg = NULL;
+ return ERR_PTR(retval);
}
/*
return retval;
}
- /* make argv[1] be the path to the binary */
+ /* make the binary the last argument to the interpreter */
retval = copy_string_kernel(bprm->interp, bprm);
if (retval < 0)
return retval;
bprm->argc++;
+ /*
+ * A single optional argument to the interpreter, inserted between it
+ * and the binary just like the argument of a #! interpreter line.
+ */
+ if (bprm->bpf_interp_arg) {
+ retval = copy_string_kernel(bprm->bpf_interp_arg, bprm);
+ if (retval < 0)
+ return retval;
+ bprm->argc++;
+ /* Consumed - don't let it leak into a nested interpreter's argv. */
+ kfree(bprm->bpf_interp_arg);
+ bprm->bpf_interp_arg = NULL;
+ }
+
/* add the interp as argv[0] */
retval = copy_string_kernel(interpreter, bprm);
if (retval < 0)