The commit is pushed to "branch-rh9-5.14.0-427.44.1.vz9.80.x-ovz" and will appear at g...@bitbucket.org:openvz/vzkernel.git after rh9-5.14.0-427.44.1.vz9.80.5 ------> commit b7afc1965873347ae723c9e81fcd76835a1da866 Author: Anthony Yznaga <anthony.yzn...@oracle.com> Date: Tue Oct 27 16:47:30 2020 -0700
oracle/exec, elf: require opt-in for accepting preserved mem Don't copy preserved VMAs to the binary being exec'd unless the binary has a "preserved-mem-ok" ELF note. Orabug: 32387875 Signed-off-by: Anthony Yznaga <anthony.yzn...@oracle.com> Reviewed-by: Liam R. Howlett <liam.howl...@oracle.com> https://virtuozzo.atlassian.net/browse/VSTOR-96305 (cherry picked from Oracle commit d1a6a6483fffe5fd7ea8d3380e1c85a52edb3d6d) Signed-off-by: Konstantin Khorenko <khore...@virtuozzo.com> Feature: oracle/mm: MADV_DOEXEC madvise() flag --- fs/binfmt_elf.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++- fs/exec.c | 17 +++++---- include/linux/binfmts.h | 7 +++- 3 files changed, 107 insertions(+), 9 deletions(-) diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index a930f0005a4f..9fe7f4cd5f6e 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -90,6 +90,8 @@ static int elf_core_dump(struct coredump_params *cprm); #define ELF_MIN_ALIGN PAGE_SIZE #endif +#define MAX_FILE_NOTE_SIZE (4*1024*1024) + #ifndef ELF_CORE_EFLAGS #define ELF_CORE_EFLAGS 0 #endif @@ -821,6 +823,78 @@ static int parse_elf_properties(struct file *f, const struct elf_phdr *phdr, return ret == -ENOENT ? 0 : ret; } +static int get_elf_notes(struct linux_binprm *bprm, struct elf_phdr *phdr, char **notes, size_t *notes_sz) +{ + char *data; + size_t datasz; + int ret; + + if (!phdr) + return 0; + + datasz = phdr->p_filesz; + if ((datasz > MAX_FILE_NOTE_SIZE) || (datasz < sizeof(struct elf_note))) + return -ENOEXEC; + + data = kvmalloc(datasz, GFP_KERNEL); + if (!data) + return -ENOMEM; + + ret = elf_read(bprm->file, data, datasz, phdr->p_offset); + if (ret < 0) { + kvfree(data); + return ret; + } + + *notes = data; + *notes_sz = datasz; + return 0; +} + +#define PRESERVED_MEM_OK_STRING "preserved-mem-ok" +#define SZ_PRESERVED_MEM_OK_STRING sizeof(PRESERVED_MEM_OK_STRING) + +static int check_preserved_mem_ok(struct linux_binprm *bprm, const char *data, const size_t datasz) +{ + size_t off = 0; + size_t remain; + + if (!data) + return 0; + + while (off < datasz) { + const struct elf_note *nhdr; + const char *name; + + remain = datasz - off; + + if (remain < sizeof(*nhdr)) + return -ENOEXEC; + + nhdr = (struct elf_note *)(data + off); + off += sizeof(*nhdr); + remain -= sizeof(*nhdr); + + if (nhdr->n_type != 0x07c1feed) { + off += roundup(nhdr->n_namesz, 4) + roundup(nhdr->n_descsz, 4); + continue; + } + + if (nhdr->n_namesz > SZ_PRESERVED_MEM_OK_STRING) + return -ENOEXEC; + + name = data + off; + if (remain < SZ_PRESERVED_MEM_OK_STRING || + strncmp(name, PRESERVED_MEM_OK_STRING, SZ_PRESERVED_MEM_OK_STRING)) + return -ENOEXEC; + + bprm->accepts_preserved_mem = 1; + break; + } + + return 0; +} + static int load_elf_binary(struct linux_binprm *bprm) { struct file *interpreter = NULL; /* to shut gcc up */ @@ -829,6 +903,9 @@ static int load_elf_binary(struct linux_binprm *bprm) unsigned long error; struct elf_phdr *elf_ppnt, *elf_phdata, *interp_elf_phdata = NULL; struct elf_phdr *elf_property_phdata = NULL; + struct elf_phdr *elf_notes_phdata = NULL; + char *elf_notes = NULL; + size_t elf_notes_sz = 0; unsigned long elf_bss, elf_brk; int bss_prot = 0; int retval, i; @@ -937,6 +1014,10 @@ static int load_elf_binary(struct linux_binprm *bprm) executable_stack = EXSTACK_DISABLE_X; break; + case PT_NOTE: + elf_notes_phdata = elf_ppnt; + break; + case PT_LOPROC ... PT_HIPROC: retval = arch_elf_pt_proc(elf_ex, elf_ppnt, bprm->file, false, @@ -998,6 +1079,14 @@ static int load_elf_binary(struct linux_binprm *bprm) if (retval) goto out_free_dentry; + retval = get_elf_notes(bprm, elf_notes_phdata, &elf_notes, &elf_notes_sz); + if (retval) + goto out_free_dentry; + + retval = check_preserved_mem_ok(bprm, elf_notes, elf_notes_sz); + if (retval) + goto out_free_dentry; + /* Flush all traces of the currently running executable */ retval = begin_new_exec(bprm); if (retval) @@ -1261,6 +1350,7 @@ static int load_elf_binary(struct linux_binprm *bprm) } } + kvfree(elf_notes); kfree(elf_phdata); set_binfmt(&elf_format); @@ -1340,6 +1430,7 @@ static int load_elf_binary(struct linux_binprm *bprm) if (interpreter) fput(interpreter); out_free_ph: + kvfree(elf_notes); kfree(elf_phdata); goto out; } @@ -1611,7 +1702,6 @@ static void fill_siginfo_note(struct memelfnote *note, user_siginfo_t *csigdata, fill_note(note, "CORE", NT_SIGINFO, sizeof(*csigdata), csigdata); } -#define MAX_FILE_NOTE_SIZE (4*1024*1024) /* * Format of NT_FILE note: * diff --git a/fs/exec.c b/fs/exec.c index c03f9d3aa347..2389863f608a 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1009,10 +1009,11 @@ static int vma_dup_some(struct mm_struct *old_mm, struct mm_struct *new_mm) * On success, this function returns with exec_update_lock * held for writing. */ -static int exec_mmap(struct mm_struct *mm) +static int exec_mmap(struct linux_binprm *bprm) { struct task_struct *tsk; struct mm_struct *old_mm, *active_mm; + struct mm_struct *mm = bprm->mm; int ret; /* Notify parent that we're no longer interested in the old VM */ @@ -1037,11 +1038,13 @@ static int exec_mmap(struct mm_struct *mm) up_write(&tsk->signal->exec_update_lock); return ret; } - ret = vma_dup_some(old_mm, mm); - if (ret) { - mmap_read_unlock(old_mm); - up_write(&tsk->signal->exec_update_lock); - return ret; + if (bprm->accepts_preserved_mem) { + ret = vma_dup_some(old_mm, mm); + if (ret) { + mmap_read_unlock(old_mm); + up_write(&tsk->signal->exec_update_lock); + return ret; + } } } @@ -1332,7 +1335,7 @@ int begin_new_exec(struct linux_binprm * bprm) * Release all of the old mmap stuff */ acct_arg_size(bprm, 0); - retval = exec_mmap(bprm->mm); + retval = exec_mmap(bprm); if (retval) goto out; diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h index 05a91f5499ba..75c9cf7387fe 100644 --- a/include/linux/binfmts.h +++ b/include/linux/binfmts.h @@ -42,7 +42,12 @@ struct linux_binprm { * Set when errors can no longer be returned to the * original userspace. */ - point_of_no_return:1; + point_of_no_return:1, + /* + * Set if the binary being exec'd will accept memory marked + * for preservation by the outgoing process. + */ + accepts_preserved_mem:1; #ifdef __alpha__ unsigned int taso:1; #endif _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel