From d8010b1175094374c8295d0fba56c2402b32a4da Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Mon, 9 Dec 2019 19:17:28 +0000 Subject: [PATCH] Copy out aux args after the argument and environment vectors. Partially revert r354741 and r354754 and go back to allocating a fixed-size chunk of stack space for the auxiliary vector. Keep sv_copyout_auxargs but change it to accept the address at the end of the environment vector as an input stack address and no longer allocate room on the stack. It is now called at the end of copyout_strings after the argv and environment vectors have been copied out. This should fix a regression in r354754 that broke the stack alignment for newer Linux amd64 binaries (and probably broke Linux arm64 as well). Reviewed by: kib Tested on: amd64 (native, linux64 (only linux-base-c7), and i386) Sponsored by: DARPA Differential Revision: https://reviews.freebsd.org/D22695 --- sys/amd64/linux/linux_sysvec.c | 38 ++++++++++++++++----------- sys/amd64/linux32/linux32_sysvec.c | 25 ++++++++++++------ sys/arm64/linux/linux_sysvec.c | 25 ++++++++++++------ sys/compat/freebsd32/freebsd32_misc.c | 17 +++++++++--- sys/i386/linux/linux_sysvec.c | 25 ++++++++++++------ sys/kern/imgact_elf.c | 7 ++--- sys/kern/kern_exec.c | 17 +++++++++--- sys/sys/imgact_elf.h | 2 +- sys/sys/sysent.h | 2 +- 9 files changed, 106 insertions(+), 52 deletions(-) diff --git a/sys/amd64/linux/linux_sysvec.c b/sys/amd64/linux/linux_sysvec.c index efdd6f36d2b2..b7fbf0768bf1 100644 --- a/sys/amd64/linux/linux_sysvec.c +++ b/sys/amd64/linux/linux_sysvec.c @@ -224,11 +224,10 @@ linux_set_syscall_retval(struct thread *td, int error) } static int -linux_copyout_auxargs(struct image_params *imgp, uintptr_t *base) +linux_copyout_auxargs(struct image_params *imgp, uintptr_t base) { Elf_Auxargs *args; Elf_Auxinfo *argarray, *pos; - u_long auxlen; struct proc *p; int error, issetugid; @@ -266,9 +265,8 @@ linux_copyout_auxargs(struct image_params *imgp, uintptr_t *base) imgp->auxargs = NULL; KASSERT(pos - argarray <= LINUX_AT_COUNT, ("Too many auxargs")); - auxlen = sizeof(*argarray) * (pos - argarray); - *base -= auxlen; - error = copyout(argarray, (void *)*base, auxlen); + error = copyout(argarray, (void *)base, + sizeof(*argarray) * LINUX_AT_COUNT); free(argarray, M_TEMP); return (error); } @@ -336,17 +334,13 @@ linux_copyout_strings(struct image_params *imgp, uintptr_t *stack_base) destp = rounddown2(destp, sizeof(void *)); ustringp = destp; - /* - * Starting with 2.24, glibc depends on a 16-byte stack alignment. - * One "long argc" will be prepended later. - */ - if (destp % 16 == 0) - destp -= 8; - if (imgp->auxargs) { - error = imgp->sysent->sv_copyout_auxargs(imgp, &destp); - if (error != 0) - return (error); + /* + * Allocate room on the stack for the ELF auxargs + * array. It has LINUX_AT_COUNT entries. + */ + destp -= LINUX_AT_COUNT * sizeof(Elf64_Auxinfo); + destp = rounddown2(destp, sizeof(void *)); } vectp = (char **)destp; @@ -357,6 +351,12 @@ linux_copyout_strings(struct image_params *imgp, uintptr_t *stack_base) */ vectp -= imgp->args->argc + 1 + imgp->args->envc + 1; + /* + * Starting with 2.24, glibc depends on a 16-byte stack alignment. + * One "long argc" will be prepended later. + */ + vectp = (char **)((((uintptr_t)vectp + 8) & ~0xF) - 8); + /* vectp also becomes our initial stack base. */ *stack_base = (uintptr_t)vectp; @@ -405,6 +405,14 @@ linux_copyout_strings(struct image_params *imgp, uintptr_t *stack_base) if (suword(vectp, 0) != 0) return (EFAULT); + if (imgp->auxargs) { + vectp++; + error = imgp->sysent->sv_copyout_auxargs(imgp, + (uintptr_t)vectp); + if (error != 0) + return (error); + } + return (0); } diff --git a/sys/amd64/linux32/linux32_sysvec.c b/sys/amd64/linux32/linux32_sysvec.c index a08372b66add..58edea923427 100644 --- a/sys/amd64/linux32/linux32_sysvec.c +++ b/sys/amd64/linux32/linux32_sysvec.c @@ -187,11 +187,10 @@ linux_translate_traps(int signal, int trap_code) } static int -linux_copyout_auxargs(struct image_params *imgp, u_long *base) +linux_copyout_auxargs(struct image_params *imgp, uintptr_t base) { Elf32_Auxargs *args; Elf32_Auxinfo *argarray, *pos; - u_long auxlen; int error, issetugid; args = (Elf32_Auxargs *)imgp->auxargs; @@ -238,9 +237,8 @@ linux_copyout_auxargs(struct image_params *imgp, u_long *base) imgp->auxargs = NULL; KASSERT(pos - argarray <= LINUX_AT_COUNT, ("Too many auxargs")); - auxlen = sizeof(*argarray) * (pos - argarray); - *base -= auxlen; - error = copyout(argarray, (void *)*base, auxlen); + error = copyout(argarray, (void *)base, + sizeof(*argarray) * LINUX_AT_COUNT); free(argarray, M_TEMP); return (error); } @@ -764,9 +762,12 @@ linux_copyout_strings(struct image_params *imgp, uintptr_t *stack_base) ustringp = destp; if (imgp->auxargs) { - error = imgp->sysent->sv_copyout_auxargs(imgp, &destp); - if (error != 0) - return (error); + /* + * Allocate room on the stack for the ELF auxargs + * array. It has LINUX_AT_COUNT entries. + */ + destp -= LINUX_AT_COUNT * sizeof(Elf32_Auxinfo); + destp = rounddown2(destp, sizeof(void *)); } vectp = (uint32_t *)destp; @@ -825,6 +826,14 @@ linux_copyout_strings(struct image_params *imgp, uintptr_t *stack_base) if (suword32(vectp, 0) != 0) return (EFAULT); + if (imgp->auxargs) { + vectp++; + error = imgp->sysent->sv_copyout_auxargs(imgp, + (uintptr_t)vectp); + if (error != 0) + return (error); + } + return (0); } diff --git a/sys/arm64/linux/linux_sysvec.c b/sys/arm64/linux/linux_sysvec.c index 69a8dcefb32f..536a59d7ec49 100644 --- a/sys/arm64/linux/linux_sysvec.c +++ b/sys/arm64/linux/linux_sysvec.c @@ -143,11 +143,10 @@ linux_set_syscall_retval(struct thread *td, int error) } static int -linux_copyout_auxargs(struct image_params *imgp, uintptr_t *base) +linux_copyout_auxargs(struct image_params *imgp, uintptr_t base) { Elf_Auxargs *args; Elf_Auxinfo *argarray, *pos; - u_long auxlen; struct proc *p; int error, issetugid; @@ -190,9 +189,8 @@ linux_copyout_auxargs(struct image_params *imgp, uintptr_t *base) imgp->auxargs = NULL; KASSERT(pos - argarray <= LINUX_AT_COUNT, ("Too many auxargs")); - auxlen = sizeof(*argarray) * (pos - argarray); - *base -= auxlen; - error = copyout(argarray, (void *)*base, auxlen); + error = copyout(argarray, (void *)base, + sizeof(*argarray) * LINUX_AT_COUNT); free(argarray, M_TEMP); return (error); } @@ -257,9 +255,12 @@ linux_copyout_strings(struct image_params *imgp, uintptr_t *stack_base) ustringp = destp; if (imgp->auxargs) { - error = imgp->sysent->sv_copyout_auxargs(imgp, &destp); - if (error != 0) - return (error); + /* + * Allocate room on the stack for the ELF auxargs + * array. It has up to LINUX_AT_COUNT entries. + */ + destp -= LINUX_AT_COUNT * sizeof(Elf64_Auxinfo); + destp = rounddown2(destp, sizeof(void *)); } vectp = (char **)destp; @@ -322,6 +323,14 @@ linux_copyout_strings(struct image_params *imgp, uintptr_t *stack_base) if (suword(vectp, 0) != 0) return (EFAULT); + if (imgp->auxargs) { + vectp++; + error = imgp->sysent->sv_copyout_auxargs(imgp, + (uintptr_t)vectp); + if (error != 0) + return (error); + } + return (0); } diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c index d7fda7834ff7..39c8f30b527f 100644 --- a/sys/compat/freebsd32/freebsd32_misc.c +++ b/sys/compat/freebsd32/freebsd32_misc.c @@ -3206,9 +3206,12 @@ freebsd32_copyout_strings(struct image_params *imgp, uintptr_t *stack_base) imgp->sysent->sv_stackgap(imgp, &destp); if (imgp->auxargs) { - error = imgp->sysent->sv_copyout_auxargs(imgp, &destp); - if (error != 0) - return (error); + /* + * Allocate room on the stack for the ELF auxargs + * array. It has up to AT_COUNT entries. + */ + destp -= AT_COUNT * sizeof(Elf32_Auxinfo); + destp = rounddown2(destp, sizeof(uint32_t)); } vectp = (uint32_t *)destp; @@ -3276,6 +3279,14 @@ freebsd32_copyout_strings(struct image_params *imgp, uintptr_t *stack_base) if (suword32(vectp, 0) != 0) return (EFAULT); + if (imgp->auxargs) { + vectp++; + error = imgp->sysent->sv_copyout_auxargs(imgp, + (uintptr_t)vectp); + if (error != 0) + return (error); + } + return (0); } diff --git a/sys/i386/linux/linux_sysvec.c b/sys/i386/linux/linux_sysvec.c index e067e1f82123..8997a3e174a6 100644 --- a/sys/i386/linux/linux_sysvec.c +++ b/sys/i386/linux/linux_sysvec.c @@ -192,14 +192,13 @@ linux_fixup(uintptr_t *stack_base, struct image_params *imgp) } static int -linux_copyout_auxargs(struct image_params *imgp, uintptr_t *base) +linux_copyout_auxargs(struct image_params *imgp, uintptr_t base) { struct proc *p; Elf32_Auxargs *args; Elf32_Auxinfo *argarray, *pos; Elf32_Addr *uplatform; struct ps_strings *arginfo; - u_long auxlen; int error, issetugid; p = imgp->proc; @@ -249,9 +248,8 @@ linux_copyout_auxargs(struct image_params *imgp, uintptr_t *base) imgp->auxargs = NULL; KASSERT(pos - argarray <= LINUX_AT_COUNT, ("Too many auxargs")); - auxlen = sizeof(*argarray) * (pos - argarray); - *base -= auxlen; - error = copyout(argarray, (void *)*base, auxlen); + error = copyout(argarray, (void *)base, + sizeof(*argarray) * LINUX_AT_COUNT); free(argarray, M_TEMP); return (error); } @@ -323,9 +321,12 @@ linux_copyout_strings(struct image_params *imgp, uintptr_t *stack_base) ustringp = destp; if (imgp->auxargs) { - error = imgp->sysent->sv_copyout_auxargs(imgp, &destp); - if (error != 0) - return (error); + /* + * Allocate room on the stack for the ELF auxargs + * array. It has LINUX_AT_COUNT entries. + */ + destp -= LINUX_AT_COUNT * sizeof(Elf32_Auxinfo); + destp = rounddown2(destp, sizeof(void *)); } vectp = (char **)destp; @@ -384,6 +385,14 @@ linux_copyout_strings(struct image_params *imgp, uintptr_t *stack_base) if (suword(vectp, 0) != 0) return (EFAULT); + if (imgp->auxargs) { + vectp++; + error = imgp->sysent->sv_copyout_auxargs(imgp, + (uintptr_t)vectp); + if (error != 0) + return (error); + } + return (0); } diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c index ebd3a45e5ba4..7ce86fbea5a7 100644 --- a/sys/kern/imgact_elf.c +++ b/sys/kern/imgact_elf.c @@ -1324,11 +1324,10 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) #define suword __CONCAT(suword, __ELF_WORD_SIZE) int -__elfN(freebsd_copyout_auxargs)(struct image_params *imgp, uintptr_t *base) +__elfN(freebsd_copyout_auxargs)(struct image_params *imgp, uintptr_t base) { Elf_Auxargs *args = (Elf_Auxargs *)imgp->auxargs; Elf_Auxinfo *argarray, *pos; - u_long auxlen; int error; argarray = pos = malloc(AT_COUNT * sizeof(*pos), M_TEMP, @@ -1374,9 +1373,7 @@ __elfN(freebsd_copyout_auxargs)(struct image_params *imgp, uintptr_t *base) imgp->auxargs = NULL; KASSERT(pos - argarray <= AT_COUNT, ("Too many auxargs")); - auxlen = sizeof(*argarray) * (pos - argarray); - *base -= auxlen; - error = copyout(argarray, (void *)*base, auxlen); + error = copyout(argarray, (void *)base, sizeof(*argarray) * AT_COUNT); free(argarray, M_TEMP); return (error); } diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c index e2611ba6f944..aaf08183e868 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -1661,9 +1661,12 @@ exec_copyout_strings(struct image_params *imgp, uintptr_t *stack_base) imgp->sysent->sv_stackgap(imgp, &destp); if (imgp->auxargs) { - error = imgp->sysent->sv_copyout_auxargs(imgp, &destp); - if (error != 0) - return (error); + /* + * Allocate room on the stack for the ELF auxargs + * array. It has up to AT_COUNT entries. + */ + destp -= AT_COUNT * sizeof(Elf_Auxinfo); + destp = rounddown2(destp, sizeof(void *)); } vectp = (char **)destp; @@ -1732,6 +1735,14 @@ exec_copyout_strings(struct image_params *imgp, uintptr_t *stack_base) if (suword(vectp, 0) != 0) return (EFAULT); + if (imgp->auxargs) { + vectp++; + error = imgp->sysent->sv_copyout_auxargs(imgp, + (uintptr_t)vectp); + if (error != 0) + return (error); + } + return (0); } diff --git a/sys/sys/imgact_elf.h b/sys/sys/imgact_elf.h index babb232964cc..1023b3c829c3 100644 --- a/sys/sys/imgact_elf.h +++ b/sys/sys/imgact_elf.h @@ -99,7 +99,7 @@ int __elfN(freebsd_fixup)(uintptr_t *, struct image_params *); int __elfN(coredump)(struct thread *, struct vnode *, off_t, int); size_t __elfN(populate_note)(int, void *, void *, size_t, void **); void __elfN(stackgap)(struct image_params *, uintptr_t *); -int __elfN(freebsd_copyout_auxargs)(struct image_params *, uintptr_t *); +int __elfN(freebsd_copyout_auxargs)(struct image_params *, uintptr_t); /* Machine specific function to dump per-thread information. */ void __elfN(dump_thread)(struct thread *, void *, size_t *); diff --git a/sys/sys/sysent.h b/sys/sys/sysent.h index 9ed77fd09bea..d6213defd005 100644 --- a/sys/sys/sysent.h +++ b/sys/sys/sysent.h @@ -111,7 +111,7 @@ struct sysentvec { int (*sv_imgact_try)(struct image_params *); void (*sv_stackgap)(struct image_params *, uintptr_t *); int (*sv_copyout_auxargs)(struct image_params *, - uintptr_t *); + uintptr_t); int sv_minsigstksz; /* minimum signal stack size */ vm_offset_t sv_minuser; /* VM_MIN_ADDRESS */ vm_offset_t sv_maxuser; /* VM_MAXUSER_ADDRESS */