aboutsummaryrefslogtreecommitdiff
path: root/include/linux
diff options
context:
space:
mode:
Diffstat (limited to 'include/linux')
-rw-r--r--include/linux/binfmt_misc.h113
-rw-r--r--include/linux/binfmts.h40
-rw-r--r--include/linux/user_namespace.h3
3 files changed, 154 insertions, 2 deletions
diff --git a/include/linux/binfmt_misc.h b/include/linux/binfmt_misc.h
new file mode 100644
index 000000000000..8045b10dd3e5
--- /dev/null
+++ b/include/linux/binfmt_misc.h
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_BINFMT_MISC_H
+#define _LINUX_BINFMT_MISC_H
+
+#include <linux/types.h>
+
+struct bpf_prog;
+struct file;
+struct linux_binprm;
+struct ucounts;
+struct user_namespace;
+
+#define BINFMT_MISC_OPS_NAME_MAX 16
+
+/* Longest name a 'B' entry can bind an interpreter under. */
+#define BINFMT_MISC_INTERP_NAME_MAX 32
+
+/* Most interpreters one entry can bind. */
+#define BINFMT_MISC_INTERP_MAX 100
+
+/**
+ * struct binfmt_misc_interp - an interpreter an entry was registered with
+ * @list: link in the entry's list, in registration order
+ * @file: the file, opened at registration and never resolved again
+ * @ucounts: the UCOUNT_BINFMT_MISC_INTERPRETERS charge the binding took
+ * @path: the path it was registered under, used as the name the interpreter
+ * runs under; stored after @name in the same allocation
+ * @name: the name the load program selects it by; empty for the fixed
+ * interpreter of a static 'F' entry
+ *
+ * Owned by the entry and living exactly as long as it does. The list head
+ * is handed to the handler's load program for the duration of one exec,
+ * which picks one with bpf_binprm_select_interp().
+ */
+struct binfmt_misc_interp {
+ struct list_head list;
+ struct file *file;
+ struct ucounts *ucounts;
+ const char *path;
+ char name[];
+};
+
+const struct binfmt_misc_interp *
+binfmt_misc_find_interp(const struct list_head *interps, const char *name);
+
+/**
+ * enum bpf_binprm_flags - per-exec invocation flags a load program can request
+ * @BPF_BINPRM_PRESERVE_ARGV0: keep the caller's argv[0] (like the 'P' flag)
+ * @BPF_BINPRM_CREDENTIALS: compute credentials from the binary; implies execfd
+ * (like the 'C' flag)
+ * @BPF_BINPRM_EXECFD: pass the binary via AT_EXECFD (like the 'O' flag)
+ * @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.
+ */
+enum bpf_binprm_flags {
+ BPF_BINPRM_PRESERVE_ARGV0 = (1ULL << 0),
+ BPF_BINPRM_CREDENTIALS = (1ULL << 1),
+ BPF_BINPRM_EXECFD = (1ULL << 2),
+ BPF_BINPRM_TRANSPARENT = (1ULL << 3),
+ BPF_BINPRM_LOADER = (1ULL << 4),
+};
+
+/**
+ * struct binfmt_misc_ops - bpf-backed binary type handler
+ * @match: decide whether the handler applies to @bprm; consulted from the
+ * entry lookup walk like static magic and extension matching, in
+ * registration order with first-match-wins semantics; sleepable,
+ * so it can read the binary to decide, but the verifier rejects
+ * the interpreter selection kfuncs in it
+ * @load: select an interpreter for the matched @bprm via
+ * bpf_binprm_set_interp(), or one the entry bound via
+ * bpf_binprm_select_interp(), and return zero; a match is
+ * committed, so a failure fails the exec instead of falling
+ * through to later entries; -ENOEXEC does not fail the exec but
+ * moves on to the remaining binary formats
+ * @name: name that 'B' entries reference the handler by
+ */
+struct binfmt_misc_ops {
+ bool (*match)(struct linux_binprm *bprm);
+ int (*load)(struct linux_binprm *bprm);
+ char name[BINFMT_MISC_OPS_NAME_MAX];
+};
+
+#ifdef CONFIG_BINFMT_MISC_BPF
+const struct binfmt_misc_ops *binfmt_misc_get_ops(struct user_namespace *user_ns,
+ const char *name);
+void binfmt_misc_put_ops(const struct binfmt_misc_ops *ops);
+bool bpf_prog_is_binfmt_misc_ops(const struct bpf_prog *prog);
+#else
+static inline const struct binfmt_misc_ops *
+binfmt_misc_get_ops(struct user_namespace *user_ns, const char *name)
+{
+ return NULL;
+}
+
+static inline void binfmt_misc_put_ops(const struct binfmt_misc_ops *ops)
+{
+}
+
+static inline bool bpf_prog_is_binfmt_misc_ops(const struct bpf_prog *prog)
+{
+ return false;
+}
+#endif /* CONFIG_BINFMT_MISC_BPF */
+
+#endif /* _LINUX_BINFMT_MISC_H */
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
index 2c77e383e737..f686a37f7a0a 100644
--- a/include/linux/binfmts.h
+++ b/include/linux/binfmts.h
@@ -12,6 +12,16 @@ struct coredump_params;
#define CORENAME_MAX_SIZE 128
+/* Interpreter selection staged by a bpf binfmt_misc handler. */
+struct binfmt_misc_bpf {
+ /* interpreters the matched entry bound, selectable by name */
+ const struct list_head *bpf_interps;
+ const char *bpf_interp; /* interpreter selected by a bpf handler */
+ struct file *bpf_interp_file; /* the bound interpreter it selected */
+ const char *bpf_interp_arg; /* interpreter argument from a bpf handler */
+ u64 bpf_flags; /* enum bpf_binprm_flags from a bpf handler */
+};
+
/*
* This structure is used to hold the arguments that are used when loading binaries.
*/
@@ -55,6 +65,7 @@ struct linux_binprm {
is_check:1;
struct file *executable; /* Executable to pass to the interpreter */
struct file *interpreter;
+ struct file *loader;
struct file *file;
struct cred *cred; /* new credentials */
int unsafe; /* how unsafe this exec is (mask of LSM_UNSAFE_*) */
@@ -65,6 +76,7 @@ struct linux_binprm {
of the time same as filename, but could be
different for binfmt_{misc,script} */
const char *fdpath; /* generated filename for execveat */
+ struct binfmt_misc_bpf; /* bpf handler interpreter selection */
unsigned interp_flags;
int execfd; /* File descriptor of the executable */
unsigned long exec;
@@ -85,6 +97,28 @@ struct linux_binprm {
#define BINPRM_FLAGS_PRESERVE_ARGV0_BIT 3
#define BINPRM_FLAGS_PRESERVE_ARGV0 (1 << BINPRM_FLAGS_PRESERVE_ARGV0_BIT)
+/* binfmt_misc dispatched to the interpreter transparently */
+#define BINPRM_FLAGS_TRANSPARENT_INTERP_BIT 4
+#define BINPRM_FLAGS_TRANSPARENT_INTERP (1 << BINPRM_FLAGS_TRANSPARENT_INTERP_BIT)
+
+/**
+ * bprm_at_flags - the AT_FLAGS this invocation implies
+ * @bprm: binary that is being executed
+ *
+ * Tell the program on the receiving end which dispatch contract it got.
+ *
+ * Return: the AT_FLAGS value for this exec
+ */
+static inline unsigned long bprm_at_flags(const struct linux_binprm *bprm)
+{
+ /* Transparency preserves the whole argv, argv[0] included. */
+ if (bprm->interp_flags & BINPRM_FLAGS_TRANSPARENT_INTERP)
+ return AT_FLAGS_TRANSPARENT_INTERP;
+ if (bprm->interp_flags & BINPRM_FLAGS_PRESERVE_ARGV0)
+ return AT_FLAGS_PRESERVE_ARGV0;
+ return 0;
+}
+
/*
* This structure defines the functions that are used to load the binary formats that
* linux accepts.
@@ -101,8 +135,8 @@ struct linux_binfmt {
#if IS_ENABLED(CONFIG_BINFMT_MISC)
struct binfmt_misc {
- struct list_head entries;
- rwlock_t entries_lock;
+ struct hlist_head entries;
+ spinlock_t entries_lock;
bool enabled;
} __randomize_layout;
@@ -129,6 +163,8 @@ extern int begin_new_exec(struct linux_binprm * bprm);
extern void setup_new_exec(struct linux_binprm * bprm);
extern void finalize_exec(struct linux_binprm *bprm);
extern void would_dump(struct linux_binprm *, struct file *);
+struct file *bprm_open_interpreter(struct linux_binprm *bprm, const char *path);
+void bprm_drop_loader(struct linux_binprm *bprm);
extern int suid_dumpable;
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 9c3be157397e..e38d9e60569f 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -58,6 +58,9 @@ enum ucount_type {
UCOUNT_FANOTIFY_GROUPS,
UCOUNT_FANOTIFY_MARKS,
#endif
+#if IS_ENABLED(CONFIG_BINFMT_MISC)
+ UCOUNT_BINFMT_MISC_INTERPRETERS,
+#endif
UCOUNT_COUNTS,
};