mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-09-05 08:44:14 +02:00
A 'B' entry's load program selects its interpreter by absolute path,
which open_exec() resolves at exec time in the mount namespace of whoever
runs the binary. The handler names an interpreter but does not get to say
which file that is. Whoever controls the filesystem view of the exec
decides that instead.
Static entries settled this long ago with 'F'. The interpreter is opened
at registration in the registrant's context and every exec runs a clone
of that file. Give a 'B' entry the same, for as many interpreters as it
needs.
An entry registered with 'D' cannot be matched yet, so it still belongs
to whoever is configuring it and can be given interpreters one write at a
time:
echo ':qemu:B::::qemu_user:D' > register
echo '+aarch64 /usr/bin/qemu-aarch64' > qemu
echo '+arm /usr/bin/qemu-arm' > qemu
echo 1 > qemu
Each path is opened by its write, with the credentials the entry file
was opened with, by the same helper that opens an 'F' interpreter. The
load program picks one per exec with bpf_binprm_select_interp() and the
entry hands out a clone of it. Nothing is resolved again, in any
namespace. The path is everything past the first space, so no
interpreter has to fit in a register string. An entry binds at most a
hundred interpreters (BINFMT_MISC_INTERP_MAX). Every binding pins a
struct file that no file descriptor accounts for, so RLIMIT_NOFILE does
not apply and some cap is needed. A hundred is plenty and raising it
later is cheap, lowering it is not.
Selection is by name so the register string and the program need not
agree on an order, and so the handler is not tied to where a distribution
puts its interpreters. A name is a single word of printable ASCII so the
entry file can report 'name path' lines. The interpreter runs under the
path it was registered under.
The entry file reads user memory once. bm_entry_write() copies the write
in and dispatches on the first byte, and parse_command() takes the copied
buffer. The status file has no binding to spell, so it keeps its own
small copy in read_command().
That moves the length cap ahead of the dispatch. A write to an entry file
longer than a binding can be is now refused with -E2BIG, and one from a
bad address reports -EFAULT, where the command parser used to report
-EINVAL for anything past three bytes.
Configurations of one instance are kept apart by the lock removal
already takes. Reading the set out of the entry file takes no lock.
Bindings are rcu-published and the open entry file pins the entry
together with everything it bound, so a reader either sees a whole node
or misses it. The interpreter is opened before the configuration lock
because resolving the path may walk this very filesystem, and only
after the command has been parsed and the name validated from the
copied buffer, so a write that can never bind opens nothing and the
errno reflects the actual failure.
Link: https://patch.msgid.link/20260730-work-binfmt_misc-preopen-v1-7-4a0b0da71f16@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
190 lines
6.2 KiB
C
190 lines
6.2 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _LINUX_BINFMTS_H
|
|
#define _LINUX_BINFMTS_H
|
|
|
|
#include <linux/sched.h>
|
|
#include <linux/unistd.h>
|
|
#include <asm/exec.h>
|
|
#include <uapi/linux/binfmts.h>
|
|
|
|
struct filename;
|
|
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.
|
|
*/
|
|
struct linux_binprm {
|
|
#ifdef CONFIG_MMU
|
|
struct vm_area_struct *vma;
|
|
unsigned long vma_pages;
|
|
unsigned long argmin; /* rlimit marker for copy_strings() */
|
|
#else
|
|
# define MAX_ARG_PAGES 32
|
|
struct page *page[MAX_ARG_PAGES];
|
|
#endif
|
|
struct mm_struct *mm;
|
|
struct mm_struct *old_mm; /* replaced address space, freed by setup_new_exec() */
|
|
/* user_ns published to task->exec_state at execve, narrowed by would_dump(). */
|
|
struct user_namespace *user_ns;
|
|
unsigned long p; /* current top of mem */
|
|
unsigned int
|
|
/* Should an execfd be passed to userspace? */
|
|
have_execfd:1,
|
|
|
|
/* Use the creds of a script (see binfmt_misc) */
|
|
execfd_creds:1,
|
|
/*
|
|
* Set by bprm_creds_for_exec hook to indicate a
|
|
* privilege-gaining exec has happened. Used to set
|
|
* AT_SECURE auxv for glibc.
|
|
*/
|
|
secureexec:1,
|
|
/*
|
|
* Set when errors can no longer be returned to the
|
|
* original userspace.
|
|
*/
|
|
point_of_no_return:1,
|
|
/* Set when "comm" must come from the dentry. */
|
|
comm_from_dentry:1,
|
|
/*
|
|
* Set by user space to check executability according to the
|
|
* caller's environment.
|
|
*/
|
|
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_*) */
|
|
unsigned int per_clear; /* bits to clear in current->personality */
|
|
int argc, envc;
|
|
const char *filename; /* Name of binary as seen by procps */
|
|
const char *interp; /* Name of the binary really executed. Most
|
|
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;
|
|
|
|
struct rlimit rlim_stack; /* Saved RLIMIT_STACK used during exec. */
|
|
|
|
char buf[BINPRM_BUF_SIZE];
|
|
} __randomize_layout;
|
|
|
|
#define BINPRM_FLAGS_ENFORCE_NONDUMP_BIT 0
|
|
#define BINPRM_FLAGS_ENFORCE_NONDUMP (1 << BINPRM_FLAGS_ENFORCE_NONDUMP_BIT)
|
|
|
|
/* filename of the binary will be inaccessible after exec */
|
|
#define BINPRM_FLAGS_PATH_INACCESSIBLE_BIT 2
|
|
#define BINPRM_FLAGS_PATH_INACCESSIBLE (1 << BINPRM_FLAGS_PATH_INACCESSIBLE_BIT)
|
|
|
|
/* preserve argv0 for the interpreter */
|
|
#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.
|
|
*/
|
|
struct linux_binfmt {
|
|
struct list_head lh;
|
|
struct module *module;
|
|
int (*load_binary)(struct linux_binprm *);
|
|
#ifdef CONFIG_COREDUMP
|
|
int (*core_dump)(struct coredump_params *cprm);
|
|
unsigned long min_coredump; /* minimal dump size */
|
|
#endif
|
|
} __randomize_layout;
|
|
|
|
#if IS_ENABLED(CONFIG_BINFMT_MISC)
|
|
struct binfmt_misc {
|
|
struct hlist_head entries;
|
|
spinlock_t entries_lock;
|
|
bool enabled;
|
|
} __randomize_layout;
|
|
|
|
extern struct binfmt_misc init_binfmt_misc;
|
|
#endif
|
|
|
|
extern void __register_binfmt(struct linux_binfmt *fmt, int insert);
|
|
|
|
/* Registration of default binfmt handlers */
|
|
static inline void register_binfmt(struct linux_binfmt *fmt)
|
|
{
|
|
__register_binfmt(fmt, 0);
|
|
}
|
|
/* Same as above, but adds a new binfmt at the top of the list */
|
|
static inline void insert_binfmt(struct linux_binfmt *fmt)
|
|
{
|
|
__register_binfmt(fmt, 1);
|
|
}
|
|
|
|
extern void unregister_binfmt(struct linux_binfmt *);
|
|
|
|
extern int __must_check remove_arg_zero(struct linux_binprm *);
|
|
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;
|
|
|
|
/* Stack area protections */
|
|
#define EXSTACK_DEFAULT 0 /* Whatever the arch defaults to */
|
|
#define EXSTACK_DISABLE_X 1 /* Disable executable stacks */
|
|
#define EXSTACK_ENABLE_X 2 /* Enable executable stacks */
|
|
|
|
extern int setup_arg_pages(struct linux_binprm * bprm,
|
|
unsigned long stack_top,
|
|
int executable_stack);
|
|
extern int transfer_args_to_stack(struct linux_binprm *bprm,
|
|
unsigned long *sp_location);
|
|
extern int bprm_change_interp(const char *interp, struct linux_binprm *bprm);
|
|
int copy_string_kernel(const char *arg, struct linux_binprm *bprm);
|
|
extern void set_binfmt(struct linux_binfmt *new);
|
|
extern ssize_t read_code(struct file *, unsigned long, loff_t, size_t);
|
|
|
|
int kernel_execve(const char *filename,
|
|
const char *const *argv, const char *const *envp);
|
|
|
|
#endif /* _LINUX_BINFMTS_H */
|