A transparent binfmt_misc dispatch hands the binary to the interpreter through AT_EXECFD and leaves the argument vector exactly as the caller built it. The loader on the receiving end has to know which contract it got. On the classic 'O'/'C' entries the binary's path is spliced into the argument vector and the loader consumes arguments. In transparent mode nothing was spliced and argv belongs entirely to the program. This cannot be inferred from AT_EXECFD alone. Raise a new AT_FLAGS bit following the AT_FLAGS_PRESERVE_ARGV0 precedent added for qemu-user in commit 2347961b11d4 ("binfmt_misc: pass binfmt_misc flags to the interpreter"). The bit also announces that mm->exe_file names the binary rather than the interpreter (added in the next commit). A loader that sees the bit may finish the identity polish by fixing up AT_PHDR/AT_ENTRY/AT_BASE in saved_auxv and fix the code/data markers via one uncapped PR_SET_MM_MAP once it has mapped the binary. I've got glibc patches for this as well but it's useful for any loader. BINPRM_FLAGS_TRANSPARENT_INTERP carries the mode from binfmt_misc to the ELF loaders. Both create_elf_tables() variants translate it into the aux vector bit. Nothing sets the bprm flag yet. Signed-off-by: Christian Brauner (Amutable) --- fs/binfmt_elf.c | 5 ++++- fs/binfmt_elf_fdpic.c | 5 ++++- include/linux/binfmts.h | 4 ++++ include/uapi/linux/binfmts.h | 7 +++++++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 16a56b6b3f6c..e44230242f3a 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -254,7 +254,10 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec, NEW_AUX_ENT(AT_PHENT, sizeof(struct elf_phdr)); NEW_AUX_ENT(AT_PHNUM, exec->e_phnum); NEW_AUX_ENT(AT_BASE, interp_load_addr); - if (bprm->interp_flags & BINPRM_FLAGS_PRESERVE_ARGV0) + /* Transparency preserves the whole argv, argv[0] included. */ + if (bprm->interp_flags & BINPRM_FLAGS_TRANSPARENT_INTERP) + flags |= AT_FLAGS_TRANSPARENT_INTERP; + else if (bprm->interp_flags & BINPRM_FLAGS_PRESERVE_ARGV0) flags |= AT_FLAGS_PRESERVE_ARGV0; NEW_AUX_ENT(AT_FLAGS, flags); NEW_AUX_ENT(AT_ENTRY, e_entry); diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index 7e3108489c83..3306d21ab738 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -645,7 +645,10 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm, NEW_AUX_ENT(AT_PHENT, sizeof(struct elf_phdr)); NEW_AUX_ENT(AT_PHNUM, exec_params->hdr.e_phnum); NEW_AUX_ENT(AT_BASE, interp_params->elfhdr_addr); - if (bprm->interp_flags & BINPRM_FLAGS_PRESERVE_ARGV0) + /* Transparency preserves the whole argv, argv[0] included. */ + if (bprm->interp_flags & BINPRM_FLAGS_TRANSPARENT_INTERP) + flags |= AT_FLAGS_TRANSPARENT_INTERP; + else if (bprm->interp_flags & BINPRM_FLAGS_PRESERVE_ARGV0) flags |= AT_FLAGS_PRESERVE_ARGV0; NEW_AUX_ENT(AT_FLAGS, flags); NEW_AUX_ENT(AT_ENTRY, exec_params->entry_addr); diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h index 03e1794b5cbb..d4f64d143d1a 100644 --- a/include/linux/binfmts.h +++ b/include/linux/binfmts.h @@ -93,6 +93,10 @@ 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) + /* * This structure defines the functions that are used to load the binary formats that * linux accepts. diff --git a/include/uapi/linux/binfmts.h b/include/uapi/linux/binfmts.h index c6f9450efc12..aafc07d78b80 100644 --- a/include/uapi/linux/binfmts.h +++ b/include/uapi/linux/binfmts.h @@ -22,4 +22,11 @@ struct pt_regs; #define AT_FLAGS_PRESERVE_ARGV0_BIT 0 #define AT_FLAGS_PRESERVE_ARGV0 (1 << AT_FLAGS_PRESERVE_ARGV0_BIT) +/* + * The interpreter runs transparently: the argument vector and the exe + * link belong to the binary passed in AT_EXECFD. + */ +#define AT_FLAGS_TRANSPARENT_INTERP_BIT 1 +#define AT_FLAGS_TRANSPARENT_INTERP (1 << AT_FLAGS_TRANSPARENT_INTERP_BIT) + #endif /* _UAPI_LINUX_BINFMTS_H */ -- 2.53.0