summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Brauner <brauner@kernel.org>2026-07-21 16:13:47 +0200
committerChristian Brauner <brauner@kernel.org>2026-08-03 10:08:44 +0200
commitc41b9cd8cf49120ae6f5f4e78d3080a697902a19 (patch)
treeee3baead76b0d338ccda961845b7559cf27ce483
parent08915b9f1837de77bda150abc43e6e26e72175e1 (diff)
downloadlinux-c41b9cd8cf49120ae6f5f4e78d3080a697902a19.tar.gz
linux-c41b9cd8cf49120ae6f5f4e78d3080a697902a19.zip
binfmt_misc: split out entry_open_interpreter() and build_interp_argv()
Opening the interpreter is a property of the matched entry: an 'F' entry hands out a clone of the file it pre-opened at registration time, any other entry opens the selected path. Give that its own helper instead of an if/else in the middle of load_misc_binary(), and let it fail early rather than carrying an ERR_PTR through the successful branch. Building the interpreter's argument vector is the bulk of what remains and the one part of load_misc_binary() that is specific to the classic dispatch. Move it into its own helper too, so the dispatch reads as what it is: pick a handler, pick an interpreter, build the invocation, open it. No functional change. Link: https://patch.msgid.link/20260721-work-bpf-binfmt_misc-ptinterp-v2-5-e57866e4ae0f@kernel.org Reviewed-by: Farid Zakaria <farid.m.zakaria@gmail.com> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
-rw-r--r--fs/binfmt_misc.c110
1 files changed, 76 insertions, 34 deletions
diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
index e87da5ece641..a47a0a677e93 100644
--- a/fs/binfmt_misc.c
+++ b/fs/binfmt_misc.c
@@ -353,35 +353,52 @@ static unsigned long entry_invocation_flags(const struct binfmt_misc_entry *e,
return flags;
}
-/*
- * the loader itself
+/**
+ * entry_open_interpreter - open the entry's interpreter for execution
+ * @e: matched binary type handler
+ * @interpreter: the interpreter selected for this exec
+ *
+ * An 'F' entry hands out a clone of the file it pre-opened at registration,
+ * any other entry opens the selected path.
+ *
+ * Return: the opened interpreter on success, an ERR_PTR on failure
*/
-static int load_misc_binary(struct linux_binprm *bprm)
+static struct file *entry_open_interpreter(const struct binfmt_misc_entry *e,
+ const char *interpreter)
{
- struct binfmt_misc_entry *fmt __free(put_binfmt_handler) = NULL;
- const char *interpreter;
- struct file *interp_file;
- struct binfmt_misc *misc;
- unsigned long flags;
+ struct file *interp_file __free(fput) = NULL;
int retval;
- misc = current_binfmt_misc();
- if (!READ_ONCE(misc->enabled))
- return -ENOEXEC;
+ if (!(e->flags & MISC_FMT_OPEN_FILE))
+ return open_exec(interpreter);
- fmt = get_binfmt_handler(misc, bprm);
- if (!fmt)
- return -ENOEXEC;
+ interp_file = file_clone_open(e->interp_file);
+ if (IS_ERR(interp_file))
+ return interp_file;
- /* Need to be able to load the file after exec */
- if (bprm->interp_flags & BINPRM_FLAGS_PATH_INACCESSIBLE)
- return -ENOENT;
+ retval = exe_file_deny_write_access(interp_file);
+ if (retval)
+ return ERR_PTR(retval);
- interpreter = entry_select_interpreter(fmt, bprm);
- if (IS_ERR(interpreter))
- return PTR_ERR(interpreter);
+ return no_free_ptr(interp_file);
+}
- flags = entry_invocation_flags(fmt, bprm);
+/**
+ * build_interp_argv - splice the interpreter invocation into the argv
+ * @bprm: binary that is being executed
+ * @interpreter: the interpreter selected for this exec
+ * @flags: invocation flags in effect for this exec
+ *
+ * The interpreter becomes argv[0] and the binary its last argument, with an
+ * optional staged argument in between. The caller's argv[0] is dropped
+ * unless 'P' keeps it.
+ *
+ * Return: 0 on success, a negative error code on failure
+ */
+static int build_interp_argv(struct linux_binprm *bprm, const char *interpreter,
+ unsigned long flags)
+{
+ int retval;
/* The entry's own choice - not one accumulated from an earlier level. */
if (flags & MISC_FMT_PRESERVE_ARGV0) {
@@ -418,24 +435,49 @@ static int load_misc_binary(struct linux_binprm *bprm)
return retval;
bprm->argc++;
+ return 0;
+}
+
+/*
+ * the loader itself
+ */
+static int load_misc_binary(struct linux_binprm *bprm)
+{
+ struct binfmt_misc_entry *fmt __free(put_binfmt_handler) = NULL;
+ const char *interpreter;
+ struct file *interp_file;
+ struct binfmt_misc *misc;
+ unsigned long flags;
+ int retval;
+
+ misc = current_binfmt_misc();
+ if (!READ_ONCE(misc->enabled))
+ return -ENOEXEC;
+
+ fmt = get_binfmt_handler(misc, bprm);
+ if (!fmt)
+ return -ENOEXEC;
+
+ /* Need to be able to load the file after exec */
+ if (bprm->interp_flags & BINPRM_FLAGS_PATH_INACCESSIBLE)
+ return -ENOENT;
+
+ interpreter = entry_select_interpreter(fmt, bprm);
+ if (IS_ERR(interpreter))
+ return PTR_ERR(interpreter);
+
+ flags = entry_invocation_flags(fmt, bprm);
+
+ retval = build_interp_argv(bprm, interpreter, flags);
+ if (retval)
+ return retval;
+
/* Update interp in case binfmt_script needs it. */
retval = bprm_change_interp(interpreter, bprm);
if (retval < 0)
return retval;
- if (fmt->flags & MISC_FMT_OPEN_FILE) {
- interp_file = file_clone_open(fmt->interp_file);
- if (!IS_ERR(interp_file)) {
- int err = exe_file_deny_write_access(interp_file);
-
- if (err) {
- fput(interp_file);
- interp_file = ERR_PTR(err);
- }
- }
- } else {
- interp_file = open_exec(interpreter);
- }
+ interp_file = entry_open_interpreter(fmt, interpreter);
if (IS_ERR(interp_file))
return PTR_ERR(interp_file);