binfmt_elf: allow arch code to examine PT_LOPROC ... PT_HIPROC headers
MIPS is introducing new variants of its O32 ABI which differ in their handling of floating point, in order to enable a gradual transition towards a world where mips32 binaries can take advantage of new hardware features only available when configured for certain FP modes. In order to do this ELF binaries are being augmented with a new section that indicates, amongst other things, the FP mode requirements of the binary. The presence & location of such a section is indicated by a program header in the PT_LOPROC ... PT_HIPROC range. In order to allow the MIPS architecture code to examine the program header & section in question, pass all program headers in this range to an architecture-specific arch_elf_pt_proc function. This function may return an error if the header is deemed invalid or unsuitable for the system, in which case that error will be returned from load_elf_binary and upwards through the execve syscall. A means is required for the architecture code to make a decision once it is known that all such headers have been seen, but before it is too late to return from an execve syscall. For this purpose the arch_check_elf function is added, and called once, after all PT_LOPROC to PT_HIPROC headers have been passed to arch_elf_pt_proc but before the code which invoked execve has been lost. This enables the architecture code to make a decision based upon all the headers present in an ELF binary and its interpreter, as is required to forbid conflicting FP ABI requirements between an ELF & its interpreter. In order to allow data to be stored throughout the calls to the above functions, struct arch_elf_state is introduced. Finally a variant of the SET_PERSONALITY macro is introduced which accepts a pointer to the struct arch_elf_state, allowing it to act based upon state observed from the architecture specific program headers. Signed-off-by: Paul Burton <paul.burton@imgtec.com> Cc: linux-mips@linux-mips.org Cc: Alexander Viro <viro@zeniv.linux.org.uk> Cc: linux-fsdevel@vger.kernel.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7679/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
parent
a9d9ef133f
commit
774c105ed8
|
@ -30,6 +30,9 @@ config COMPAT_BINFMT_ELF
|
|||
config ARCH_BINFMT_ELF_RANDOMIZE_PIE
|
||||
bool
|
||||
|
||||
config ARCH_BINFMT_ELF_STATE
|
||||
bool
|
||||
|
||||
config BINFMT_ELF_FDPIC
|
||||
bool "Kernel support for FDPIC ELF binaries"
|
||||
default y
|
||||
|
|
105
fs/binfmt_elf.c
105
fs/binfmt_elf.c
|
@ -440,6 +440,74 @@ static struct elf_phdr *load_elf_phdrs(struct elfhdr *elf_ex,
|
|||
return elf_phdata;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_ARCH_BINFMT_ELF_STATE
|
||||
|
||||
/**
|
||||
* struct arch_elf_state - arch-specific ELF loading state
|
||||
*
|
||||
* This structure is used to preserve architecture specific data during
|
||||
* the loading of an ELF file, throughout the checking of architecture
|
||||
* specific ELF headers & through to the point where the ELF load is
|
||||
* known to be proceeding (ie. SET_PERSONALITY).
|
||||
*
|
||||
* This implementation is a dummy for architectures which require no
|
||||
* specific state.
|
||||
*/
|
||||
struct arch_elf_state {
|
||||
};
|
||||
|
||||
#define INIT_ARCH_ELF_STATE {}
|
||||
|
||||
/**
|
||||
* arch_elf_pt_proc() - check a PT_LOPROC..PT_HIPROC ELF program header
|
||||
* @ehdr: The main ELF header
|
||||
* @phdr: The program header to check
|
||||
* @elf: The open ELF file
|
||||
* @is_interp: True if the phdr is from the interpreter of the ELF being
|
||||
* loaded, else false.
|
||||
* @state: Architecture-specific state preserved throughout the process
|
||||
* of loading the ELF.
|
||||
*
|
||||
* Inspects the program header phdr to validate its correctness and/or
|
||||
* suitability for the system. Called once per ELF program header in the
|
||||
* range PT_LOPROC to PT_HIPROC, for both the ELF being loaded and its
|
||||
* interpreter.
|
||||
*
|
||||
* Return: Zero to proceed with the ELF load, non-zero to fail the ELF load
|
||||
* with that return code.
|
||||
*/
|
||||
static inline int arch_elf_pt_proc(struct elfhdr *ehdr,
|
||||
struct elf_phdr *phdr,
|
||||
struct file *elf, bool is_interp,
|
||||
struct arch_elf_state *state)
|
||||
{
|
||||
/* Dummy implementation, always proceed */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* arch_check_elf() - check a PT_LOPROC..PT_HIPROC ELF program header
|
||||
* @ehdr: The main ELF header
|
||||
* @has_interp: True if the ELF has an interpreter, else false.
|
||||
* @state: Architecture-specific state preserved throughout the process
|
||||
* of loading the ELF.
|
||||
*
|
||||
* Provides a final opportunity for architecture code to reject the loading
|
||||
* of the ELF & cause an exec syscall to return an error. This is called after
|
||||
* all program headers to be checked by arch_elf_pt_proc have been.
|
||||
*
|
||||
* Return: Zero to proceed with the ELF load, non-zero to fail the ELF load
|
||||
* with that return code.
|
||||
*/
|
||||
static inline int arch_check_elf(struct elfhdr *ehdr, bool has_interp,
|
||||
struct arch_elf_state *state)
|
||||
{
|
||||
/* Dummy implementation, always proceed */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* !CONFIG_ARCH_BINFMT_ELF_STATE */
|
||||
|
||||
/* This is much more generalized than the library routine read function,
|
||||
so we keep this separate. Technically the library read function
|
||||
is only provided so that we can read a.out libraries that have
|
||||
|
@ -611,6 +679,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
|
|||
struct elfhdr elf_ex;
|
||||
struct elfhdr interp_elf_ex;
|
||||
} *loc;
|
||||
struct arch_elf_state arch_state = INIT_ARCH_ELF_STATE;
|
||||
|
||||
loc = kmalloc(sizeof(*loc), GFP_KERNEL);
|
||||
if (!loc) {
|
||||
|
@ -705,12 +774,21 @@ static int load_elf_binary(struct linux_binprm *bprm)
|
|||
|
||||
elf_ppnt = elf_phdata;
|
||||
for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++)
|
||||
if (elf_ppnt->p_type == PT_GNU_STACK) {
|
||||
switch (elf_ppnt->p_type) {
|
||||
case PT_GNU_STACK:
|
||||
if (elf_ppnt->p_flags & PF_X)
|
||||
executable_stack = EXSTACK_ENABLE_X;
|
||||
else
|
||||
executable_stack = EXSTACK_DISABLE_X;
|
||||
break;
|
||||
|
||||
case PT_LOPROC ... PT_HIPROC:
|
||||
retval = arch_elf_pt_proc(&loc->elf_ex, elf_ppnt,
|
||||
bprm->file, false,
|
||||
&arch_state);
|
||||
if (retval)
|
||||
goto out_free_dentry;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Some simple consistency checks for the interpreter */
|
||||
|
@ -728,8 +806,30 @@ static int load_elf_binary(struct linux_binprm *bprm)
|
|||
interpreter);
|
||||
if (!interp_elf_phdata)
|
||||
goto out_free_dentry;
|
||||
|
||||
/* Pass PT_LOPROC..PT_HIPROC headers to arch code */
|
||||
elf_ppnt = interp_elf_phdata;
|
||||
for (i = 0; i < loc->interp_elf_ex.e_phnum; i++, elf_ppnt++)
|
||||
switch (elf_ppnt->p_type) {
|
||||
case PT_LOPROC ... PT_HIPROC:
|
||||
retval = arch_elf_pt_proc(&loc->interp_elf_ex,
|
||||
elf_ppnt, interpreter,
|
||||
true, &arch_state);
|
||||
if (retval)
|
||||
goto out_free_dentry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow arch code to reject the ELF at this point, whilst it's
|
||||
* still possible to return an error to the code that invoked
|
||||
* the exec syscall.
|
||||
*/
|
||||
retval = arch_check_elf(&loc->elf_ex, !!interpreter, &arch_state);
|
||||
if (retval)
|
||||
goto out_free_dentry;
|
||||
|
||||
/* Flush all traces of the currently running executable */
|
||||
retval = flush_old_exec(bprm);
|
||||
if (retval)
|
||||
|
@ -737,7 +837,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
|
|||
|
||||
/* Do this immediately, since STACK_TOP as used in setup_arg_pages
|
||||
may depend on the personality. */
|
||||
SET_PERSONALITY(loc->elf_ex);
|
||||
SET_PERSONALITY2(loc->elf_ex, &arch_state);
|
||||
if (elf_read_implies_exec(loc->elf_ex, executable_stack))
|
||||
current->personality |= READ_IMPLIES_EXEC;
|
||||
|
||||
|
@ -929,6 +1029,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
|
|||
}
|
||||
}
|
||||
|
||||
kfree(interp_elf_phdata);
|
||||
kfree(elf_phdata);
|
||||
|
||||
set_binfmt(&elf_format);
|
||||
|
|
|
@ -15,6 +15,11 @@
|
|||
set_personality(PER_LINUX | (current->personality & (~PER_MASK)))
|
||||
#endif
|
||||
|
||||
#ifndef SET_PERSONALITY2
|
||||
#define SET_PERSONALITY2(ex, state) \
|
||||
SET_PERSONALITY(ex)
|
||||
#endif
|
||||
|
||||
#if ELF_CLASS == ELFCLASS32
|
||||
|
||||
extern Elf32_Dyn _DYNAMIC [];
|
||||
|
|
Loading…
Reference in New Issue