diff options
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/uts/common/brand/sn1/sn1_brand.c | 7 | ||||
-rw-r--r-- | usr/src/uts/common/brand/solaris10/s10_brand.c | 8 | ||||
-rw-r--r-- | usr/src/uts/common/exec/elf/elf.c | 1286 | ||||
-rw-r--r-- | usr/src/uts/common/exec/elf/elf_impl.h | 17 | ||||
-rw-r--r-- | usr/src/uts/common/exec/intp/intp.c | 9 | ||||
-rw-r--r-- | usr/src/uts/common/exec/java/java.c | 3 | ||||
-rw-r--r-- | usr/src/uts/common/exec/shbin/shbin.c | 5 | ||||
-rw-r--r-- | usr/src/uts/common/fs/proc/prioctl.c | 10 | ||||
-rw-r--r-- | usr/src/uts/common/fs/proc/prsubr.c | 21 | ||||
-rw-r--r-- | usr/src/uts/common/os/brand.c | 3 | ||||
-rw-r--r-- | usr/src/uts/common/os/core.c | 22 | ||||
-rw-r--r-- | usr/src/uts/common/os/exec.c | 8 | ||||
-rw-r--r-- | usr/src/uts/common/sys/brand.h | 12 | ||||
-rw-r--r-- | usr/src/uts/common/sys/exec.h | 26 | ||||
-rw-r--r-- | usr/src/uts/common/sys/prsystm.h | 4 |
15 files changed, 807 insertions, 634 deletions
diff --git a/usr/src/uts/common/brand/sn1/sn1_brand.c b/usr/src/uts/common/brand/sn1/sn1_brand.c index d61928d578..02d293fcb2 100644 --- a/usr/src/uts/common/brand/sn1/sn1_brand.c +++ b/usr/src/uts/common/brand/sn1/sn1_brand.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2019 Joyent, Inc. */ #include <sys/errno.h> @@ -57,7 +58,7 @@ void sn1_forklwp(klwp_t *, klwp_t *); void sn1_freelwp(klwp_t *); void sn1_lwpexit(klwp_t *); int sn1_elfexec(vnode_t *, execa_t *, uarg_t *, intpdata_t *, int, - long *, int, caddr_t, cred_t *, int); + size_t *, int, caddr_t, cred_t *, int); /* sn1 brand */ struct brand_ops sn1_brops = { @@ -220,8 +221,8 @@ sn1_init_brand_data(zone_t *zone) int sn1_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, - int level, long *execsz, int setid, caddr_t exec_file, cred_t *cred, - int brand_action) + int level, size_t *execsz, int setid, caddr_t exec_file, cred_t *cred, + int brand_action) { return (brand_solaris_elfexec(vp, uap, args, idatap, level, execsz, setid, exec_file, cred, brand_action, &sn1_brand, SN1_BRANDNAME, diff --git a/usr/src/uts/common/brand/solaris10/s10_brand.c b/usr/src/uts/common/brand/solaris10/s10_brand.c index 0841f02e51..b3ea043cdb 100644 --- a/usr/src/uts/common/brand/solaris10/s10_brand.c +++ b/usr/src/uts/common/brand/solaris10/s10_brand.c @@ -22,7 +22,7 @@ /* * Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved. * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2018, Joyent, Inc. + * Copyright 2019 Joyent, Inc. */ #include <sys/errno.h> @@ -61,7 +61,7 @@ void s10_forklwp(klwp_t *, klwp_t *); void s10_freelwp(klwp_t *); void s10_lwpexit(klwp_t *); int s10_elfexec(vnode_t *, execa_t *, uarg_t *, intpdata_t *, int, - long *, int, caddr_t, cred_t *, int); + size_t *, int, caddr_t, cred_t *, int); void s10_sigset_native_to_s10(sigset_t *); void s10_sigset_s10_to_native(sigset_t *); @@ -390,8 +390,8 @@ s10_init_brand_data(zone_t *zone) int s10_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, - int level, long *execsz, int setid, caddr_t exec_file, cred_t *cred, - int brand_action) + int level, size_t *execsz, int setid, caddr_t exec_file, cred_t *cred, + int brand_action) { return (brand_solaris_elfexec(vp, uap, args, idatap, level, execsz, setid, exec_file, cred, brand_action, &s10_brand, S10_BRANDNAME, diff --git a/usr/src/uts/common/exec/elf/elf.c b/usr/src/uts/common/exec/elf/elf.c index 4f888d6feb..73d302aaa5 100644 --- a/usr/src/uts/common/exec/elf/elf.c +++ b/usr/src/uts/common/exec/elf/elf.c @@ -26,7 +26,7 @@ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* - * Copyright (c) 2019, Joyent, Inc. + * Copyright 2019, Joyent, Inc. * Copyright 2022 Oxide Computer Company */ @@ -83,15 +83,33 @@ extern volatile size_t aslr_max_brk_skew; #define ORIGIN_STR "ORIGIN" #define ORIGIN_STR_SIZE 6 -static int getelfhead(vnode_t *, cred_t *, Ehdr *, int *, int *, int *); -static int getelfphdr(vnode_t *, cred_t *, const Ehdr *, int, caddr_t *, - ssize_t *); -static int getelfshdr(vnode_t *, cred_t *, const Ehdr *, int, int, caddr_t *, - ssize_t *, caddr_t *, ssize_t *); -static size_t elfsize(Ehdr *, int, caddr_t, uintptr_t *); -static int mapelfexec(vnode_t *, Ehdr *, int, caddr_t, - Phdr **, Phdr **, Phdr **, Phdr **, Phdr *, - caddr_t *, caddr_t *, intptr_t *, intptr_t *, size_t, long *, size_t *); +static int getelfhead(vnode_t *, cred_t *, Ehdr *, uint_t *, uint_t *, + uint_t *); +static int getelfphdr(vnode_t *, cred_t *, const Ehdr *, uint_t, caddr_t *, + size_t *); +static int getelfshdr(vnode_t *, cred_t *, const Ehdr *, uint_t, uint_t, + caddr_t *, size_t *, caddr_t *, size_t *); +static size_t elfsize(const Ehdr *, uint_t, const caddr_t, uintptr_t *); +static int mapelfexec(vnode_t *, Ehdr *, uint_t, caddr_t, Phdr **, Phdr **, + Phdr **, Phdr **, Phdr *, caddr_t *, caddr_t *, intptr_t *, uintptr_t *, + size_t, size_t *, size_t *); + + +#ifdef _ELF32_COMPAT +/* Link against the non-compat instances when compiling the 32-bit version. */ +extern size_t elf_datasz_max; +extern size_t elf_zeropg_sz; +extern void elf_ctx_resize_scratch(elf_core_ctx_t *, size_t); +extern uint_t elf_nphdr_max; +extern uint_t elf_nshdr_max; +extern size_t elf_shstrtab_max; +#else +size_t elf_datasz_max = 1 * 1024 * 1024; +size_t elf_zeropg_sz = 4 * 1024; +uint_t elf_nphdr_max = 1000; +uint_t elf_nshdr_max = 10000; +size_t elf_shstrtab_max = 100 * 1024; +#endif static int dtrace_safe_phdr(Phdr *phdrp, struct uarg *args, uintptr_t base) @@ -146,6 +164,22 @@ handle_secflag_dt(proc_t *p, uint_t dt, uint_t val) return (0); } +#ifndef _ELF32_COMPAT +void +elf_ctx_resize_scratch(elf_core_ctx_t *ctx, size_t sz) +{ + size_t target = MIN(sz, elf_datasz_max); + + if (target > ctx->ecc_bufsz) { + if (ctx->ecc_buf != NULL) { + kmem_free(ctx->ecc_buf, ctx->ecc_bufsz); + } + ctx->ecc_buf = kmem_alloc(target, KM_SLEEP); + ctx->ecc_bufsz = target; + } +} +#endif /* _ELF32_COMPAT */ + /* * Map in the executable pointed to by vp. Returns 0 on success. */ @@ -154,19 +188,17 @@ mapexec_brand(vnode_t *vp, uarg_t *args, Ehdr *ehdr, Addr *uphdr_vaddr, intptr_t *voffset, caddr_t exec_file, int *interp, caddr_t *bssbase, caddr_t *brkbase, size_t *brksize, uintptr_t *lddatap) { - size_t len; + size_t len, phdrsize; struct vattr vat; caddr_t phdrbase = NULL; - ssize_t phdrsize; - int nshdrs, shstrndx, nphdrs; + uint_t nshdrs, shstrndx, nphdrs; int error = 0; Phdr *uphdr = NULL; Phdr *junk = NULL; Phdr *dynphdr = NULL; Phdr *dtrphdr = NULL; - uintptr_t lddata; - long execsz; - intptr_t minaddr; + uintptr_t lddata, minaddr; + size_t execsz; if (lddatap != NULL) *lddatap = 0; @@ -196,6 +228,8 @@ mapexec_brand(vnode_t *vp, uarg_t *args, Ehdr *ehdr, Addr *uphdr_vaddr, &junk, &dtrphdr, NULL, bssbase, brkbase, voffset, &minaddr, len, &execsz, brksize)) { uprintf("%s: Cannot map %s\n", exec_file, args->pathname); + if (uphdr != NULL && uphdr->p_flags == 0) + kmem_free(uphdr, sizeof (Phdr)); kmem_free(phdrbase, phdrsize); return (error); } @@ -215,6 +249,9 @@ mapexec_brand(vnode_t *vp, uarg_t *args, Ehdr *ehdr, Addr *uphdr_vaddr, if (uphdr != NULL) { *uphdr_vaddr = uphdr->p_vaddr; + + if (uphdr->p_flags == 0) + kmem_free(uphdr, sizeof (Phdr)); } else { *uphdr_vaddr = (Addr)-1; } @@ -223,17 +260,16 @@ mapexec_brand(vnode_t *vp, uarg_t *args, Ehdr *ehdr, Addr *uphdr_vaddr, return (error); } -/*ARGSUSED*/ int elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, - int level, long *execsz, int setid, caddr_t exec_file, cred_t *cred, + int level, size_t *execsz, int setid, caddr_t exec_file, cred_t *cred, int brand_action) { caddr_t phdrbase = NULL; caddr_t bssbase = 0; caddr_t brkbase = 0; size_t brksize = 0; - ssize_t dlnsize; + size_t dlnsize; aux_entry_t *aux; int error; ssize_t resid; @@ -245,20 +281,19 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, Phdr *uphdr = NULL; Phdr *junk = NULL; size_t len; + size_t postfixsize = 0; size_t i; - ssize_t phdrsize; - int postfixsize = 0; - int hsize; Phdr *phdrp; Phdr *dataphdrp = NULL; Phdr *dtrphdr; Phdr *capphdr = NULL; Cap *cap = NULL; - ssize_t capsize; + size_t capsize; int hasu = 0; int hasauxv = 0; int hasintp = 0; int branded = 0; + boolean_t dynuphdr = B_FALSE; struct proc *p = ttoproc(curthread); struct user *up = PTOU(p); @@ -271,7 +306,8 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, struct execenv exenv; } *bigwad; /* kmem_alloc this behemoth so we don't blow stack */ Ehdr *ehdrp; - int nshdrs, shstrndx, nphdrs; + uint_t nshdrs, shstrndx, nphdrs; + size_t phdrsize; char *dlnp; char *pathbufp; rlim64_t limit; @@ -351,7 +387,7 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, * determine any non-default stack protections, * and still have this code be machine independent. */ - hsize = ehdrp->e_phentsize; + const uint_t hsize = ehdrp->e_phentsize; phdrp = (Phdr *)phdrbase; for (i = nphdrs; i > 0; i--) { switch (phdrp->p_type) { @@ -507,7 +543,7 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, if ((error = vn_rdwr(UIO_READ, vp, (caddr_t)dyn, (ssize_t)dynsize, (offset_t)(dynoffset + i), UIO_SYSSPACE, 0, (rlim64_t)0, - CRED(), &resid)) != 0) { + CRED(), NULL)) != 0) { uprintf("%s: cannot read .dynamic section\n", exec_file); goto out; @@ -535,13 +571,13 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, if (capphdr != NULL && (capsize = capphdr->p_filesz) > 0 && capsize <= 16 * sizeof (*cap)) { - int ncaps = capsize / sizeof (*cap); + const uint_t ncaps = capsize / sizeof (*cap); Cap *cp; cap = kmem_alloc(capsize, KM_SLEEP); if ((error = vn_rdwr(UIO_READ, vp, (caddr_t)cap, - capsize, (offset_t)capphdr->p_offset, - UIO_SYSSPACE, 0, (rlim64_t)0, CRED(), &resid)) != 0) { + (ssize_t)capsize, (offset_t)capphdr->p_offset, + UIO_SYSSPACE, 0, (rlim64_t)0, CRED(), NULL)) != 0) { uprintf("%s: Cannot read capabilities section\n", exec_file); goto out; @@ -581,9 +617,18 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, dtrphdr = NULL; - if ((error = mapelfexec(vp, ehdrp, nphdrs, phdrbase, &uphdr, &intphdr, + error = mapelfexec(vp, ehdrp, nphdrs, phdrbase, &uphdr, &intphdr, &stphdr, &dtrphdr, dataphdrp, &bssbase, &brkbase, &voffset, NULL, - len, execsz, &brksize)) != 0) + len, execsz, &brksize); + + /* + * Our uphdr has been dynamically allocated if (and only if) its + * program header flags are clear. To avoid leaks, this must be + * checked regardless of whether mapelfexec() emitted an error. + */ + dynuphdr = (uphdr != NULL && uphdr->p_flags == 0); + + if (error != 0) goto bad; if (uphdr != NULL && intphdr == NULL) @@ -602,15 +647,21 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, dlnsize = intphdr->p_filesz; - if (dlnsize > MAXPATHLEN || dlnsize <= 0) + /* + * Make sure none of the component pieces of dlnsize result in + * an oversized or zeroed result. + */ + if (intphdr->p_filesz > MAXPATHLEN || dlnsize > MAXPATHLEN || + dlnsize == 0 || dlnsize < intphdr->p_filesz) { goto bad; + } /* * Read in "interpreter" pathname. */ - if ((error = vn_rdwr(UIO_READ, vp, dlnp, intphdr->p_filesz, - (offset_t)intphdr->p_offset, UIO_SYSSPACE, 0, (rlim64_t)0, - CRED(), &resid)) != 0) { + if ((error = vn_rdwr(UIO_READ, vp, dlnp, + (ssize_t)intphdr->p_filesz, (offset_t)intphdr->p_offset, + UIO_SYSSPACE, 0, (rlim64_t)0, CRED(), &resid)) != 0) { uprintf("%s: Cannot obtain interpreter pathname\n", exec_file); goto bad; @@ -755,9 +806,10 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, dtrphdr = NULL; - error = mapelfexec(nvp, ehdrp, nphdrs, phdrbase, &junk, &junk, + error = mapelfexec(nvp, ehdrp, nphdrs, phdrbase, NULL, &junk, &junk, &dtrphdr, NULL, NULL, NULL, &voffset, NULL, len, execsz, NULL); + if (error || junk != NULL) { VN_RELE(nvp); uprintf("%s: Cannot map %s\n", exec_file, dlnp); @@ -891,7 +943,7 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, #endif /* defined(__amd64) */ ADDAUX(aux, AT_NULL, 0) - postfixsize = (char *)aux - (char *)bigwad->elfargs; + postfixsize = (uintptr_t)aux - (uintptr_t)bigwad->elfargs; /* * We make assumptions above when we determine how many aux @@ -902,8 +954,8 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, * We detect that now and error out. */ if (postfixsize != args->auxsize) { - DTRACE_PROBE2(elfexec_badaux, int, postfixsize, - int, args->auxsize); + DTRACE_PROBE2(elfexec_badaux, size_t, postfixsize, + size_t, args->auxsize); goto bad; } ASSERT(postfixsize <= __KERN_NAUXV_IMPL * sizeof (aux_entry_t)); @@ -931,7 +983,7 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, bzero(up->u_auxv, sizeof (up->u_auxv)); up->u_commpagep = args->commpage; if (postfixsize) { - int num_auxv; + size_t num_auxv; /* * Copy the aux vector to the user stack. @@ -996,6 +1048,8 @@ bad: if (error == 0) error = ENOEXEC; out: + if (dynuphdr) + kmem_free(uphdr, sizeof (Phdr)); if (phdrbase != NULL) kmem_free(phdrbase, phdrsize); if (cap != NULL) @@ -1008,32 +1062,23 @@ out: * Compute the memory size requirement for the ELF file. */ static size_t -elfsize(Ehdr *ehdrp, int nphdrs, caddr_t phdrbase, uintptr_t *lddata) +elfsize(const Ehdr *ehdrp, uint_t nphdrs, const caddr_t phdrbase, + uintptr_t *lddata) { - size_t len; - Phdr *phdrp = (Phdr *)phdrbase; - int hsize = ehdrp->e_phentsize; - int first = 1; - int dfirst = 1; /* first data segment */ - uintptr_t loaddr = 0; + const Phdr *phdrp = (Phdr *)phdrbase; + const uint_t hsize = ehdrp->e_phentsize; + boolean_t dfirst = B_TRUE; + uintptr_t loaddr = UINTPTR_MAX; uintptr_t hiaddr = 0; - uintptr_t lo, hi; - int i; + uint_t i; for (i = nphdrs; i > 0; i--) { if (phdrp->p_type == PT_LOAD) { - lo = phdrp->p_vaddr; - hi = lo + phdrp->p_memsz; - if (first) { - loaddr = lo; - hiaddr = hi; - first = 0; - } else { - if (loaddr > lo) - loaddr = lo; - if (hiaddr < hi) - hiaddr = hi; - } + const uintptr_t lo = phdrp->p_vaddr; + const uintptr_t hi = lo + phdrp->p_memsz; + + loaddr = MIN(lo, loaddr); + hiaddr = MAX(hi, hiaddr); /* * save the address of the first data segment @@ -1043,16 +1088,18 @@ elfsize(Ehdr *ehdrp, int nphdrs, caddr_t phdrbase, uintptr_t *lddata) if ((lddata != NULL) && dfirst && (phdrp->p_flags & PF_W)) { *lddata = lo; - dfirst = 0; + dfirst = B_FALSE; } } phdrp = (Phdr *)((caddr_t)phdrp + hsize); } - len = hiaddr - (loaddr & PAGEMASK); - len = roundup(len, PAGESIZE); + if (hiaddr <= loaddr) { + /* No non-zero PT_LOAD segment found */ + return (0); + } - return (len); + return (roundup(hiaddr - (loaddr & PAGEMASK), PAGESIZE)); } /* @@ -1062,8 +1109,8 @@ elfsize(Ehdr *ehdrp, int nphdrs, caddr_t phdrbase, uintptr_t *lddata) * EINVAL Format recognized but execution not supported */ static int -getelfhead(vnode_t *vp, cred_t *credp, Ehdr *ehdr, int *nshdrs, int *shstrndx, - int *nphdrs) +getelfhead(vnode_t *vp, cred_t *credp, Ehdr *ehdr, uint_t *nshdrs, + uint_t *shstrndx, uint_t *nphdrs) { int error; ssize_t resid; @@ -1103,7 +1150,7 @@ getelfhead(vnode_t *vp, cred_t *credp, Ehdr *ehdr, int *nshdrs, int *shstrndx, /* * If e_shnum, e_shstrndx, or e_phnum is its sentinel value, we need - * to read in the section header at index zero to acces the true + * to read in the section header at index zero to access the true * values for those fields. */ if ((*nshdrs == 0 && ehdr->e_shoff != 0) || @@ -1115,8 +1162,9 @@ getelfhead(vnode_t *vp, cred_t *credp, Ehdr *ehdr, int *nshdrs, int *shstrndx, if ((error = vn_rdwr(UIO_READ, vp, (caddr_t)&shdr, sizeof (shdr), (offset_t)ehdr->e_shoff, UIO_SYSSPACE, 0, - (rlim64_t)0, credp, &resid)) != 0) + (rlim64_t)0, credp, NULL)) != 0) { return (error); + } if (*nshdrs == 0) *nshdrs = shdr.sh_size; @@ -1129,33 +1177,29 @@ getelfhead(vnode_t *vp, cred_t *credp, Ehdr *ehdr, int *nshdrs, int *shstrndx, return (0); } -#ifdef _ELF32_COMPAT -extern size_t elf_nphdr_max; +/* + * We use members through p_flags on 32-bit files and p_memsz on 64-bit files, + * so e_phentsize must be at least large enough to include those members. + */ +#if !defined(_LP64) || defined(_ELF32_COMPAT) +#define MINPHENTSZ (offsetof(Phdr, p_flags) + \ + sizeof (((Phdr *)NULL)->p_flags)) #else -size_t elf_nphdr_max = 1000; +#define MINPHENTSZ (offsetof(Phdr, p_memsz) + \ + sizeof (((Phdr *)NULL)->p_memsz)) #endif static int -getelfphdr(vnode_t *vp, cred_t *credp, const Ehdr *ehdr, int nphdrs, - caddr_t *phbasep, ssize_t *phsizep) +getelfphdr(vnode_t *vp, cred_t *credp, const Ehdr *ehdr, uint_t nphdrs, + caddr_t *phbasep, size_t *phsizep) { - ssize_t resid, minsize; int err; /* - * Since we're going to be using e_phentsize to iterate down the - * array of program headers, it must be 8-byte aligned or else - * a we might cause a misaligned access. We use all members through - * p_flags on 32-bit ELF files and p_memsz on 64-bit ELF files so - * e_phentsize must be at least large enough to include those - * members. + * Ensure that e_phentsize is large enough for required fields to be + * accessible and will maintain 8-byte alignment. */ -#if !defined(_LP64) || defined(_ELF32_COMPAT) - minsize = offsetof(Phdr, p_flags) + sizeof (((Phdr *)NULL)->p_flags); -#else - minsize = offsetof(Phdr, p_memsz) + sizeof (((Phdr *)NULL)->p_memsz); -#endif - if (ehdr->e_phentsize < minsize || (ehdr->e_phentsize & 3)) + if (ehdr->e_phentsize < MINPHENTSZ || (ehdr->e_phentsize & 3)) return (EINVAL); *phsizep = nphdrs * ehdr->e_phentsize; @@ -1167,9 +1211,9 @@ getelfphdr(vnode_t *vp, cred_t *credp, const Ehdr *ehdr, int nphdrs, *phbasep = kmem_alloc(*phsizep, KM_SLEEP); } - if ((err = vn_rdwr(UIO_READ, vp, *phbasep, *phsizep, + if ((err = vn_rdwr(UIO_READ, vp, *phbasep, (ssize_t)*phsizep, (offset_t)ehdr->e_phoff, UIO_SYSSPACE, 0, (rlim64_t)0, - credp, &resid)) != 0) { + credp, NULL)) != 0) { kmem_free(*phbasep, *phsizep); *phbasep = NULL; return (err); @@ -1178,21 +1222,14 @@ getelfphdr(vnode_t *vp, cred_t *credp, const Ehdr *ehdr, int nphdrs, return (0); } -#ifdef _ELF32_COMPAT -extern size_t elf_nshdr_max; -extern size_t elf_shstrtab_max; -#else -size_t elf_nshdr_max = 10000; -size_t elf_shstrtab_max = 100 * 1024; -#endif - +#define MINSHDRSZ (offsetof(Shdr, sh_entsize) + \ + sizeof (((Shdr *)NULL)->sh_entsize)) static int -getelfshdr(vnode_t *vp, cred_t *credp, const Ehdr *ehdr, - int nshdrs, int shstrndx, caddr_t *shbasep, ssize_t *shsizep, - char **shstrbasep, ssize_t *shstrsizep) +getelfshdr(vnode_t *vp, cred_t *credp, const Ehdr *ehdr, uint_t nshdrs, + uint_t shstrndx, caddr_t *shbasep, size_t *shsizep, char **shstrbasep, + size_t *shstrsizep) { - ssize_t resid, minsize; int err; Shdr *shdr; @@ -1204,10 +1241,10 @@ getelfshdr(vnode_t *vp, cred_t *credp, const Ehdr *ehdr, * must be at least large enough to include that member. The index * of the string table section must also be valid. */ - minsize = offsetof(Shdr, sh_entsize) + sizeof (shdr->sh_entsize); - if (ehdr->e_shentsize < minsize || (ehdr->e_shentsize & 3) || - shstrndx >= nshdrs) + if (ehdr->e_shentsize < MINSHDRSZ || (ehdr->e_shentsize & 3) || + nshdrs == 0 || shstrndx >= nshdrs) { return (EINVAL); + } *shsizep = nshdrs * ehdr->e_shentsize; @@ -1218,16 +1255,16 @@ getelfshdr(vnode_t *vp, cred_t *credp, const Ehdr *ehdr, *shbasep = kmem_alloc(*shsizep, KM_SLEEP); } - if ((err = vn_rdwr(UIO_READ, vp, *shbasep, *shsizep, + if ((err = vn_rdwr(UIO_READ, vp, *shbasep, (ssize_t)*shsizep, (offset_t)ehdr->e_shoff, UIO_SYSSPACE, 0, (rlim64_t)0, - credp, &resid)) != 0) { + credp, NULL)) != 0) { kmem_free(*shbasep, *shsizep); return (err); } /* - * Pull the section string table out of the vnode; fail if the size - * is zero. + * Grab the section string table. Walking through the shdrs is + * pointless if their names cannot be interrogated. */ shdr = (Shdr *)(*shbasep + shstrndx * ehdr->e_shentsize); if ((*shstrsizep = shdr->sh_size) == 0) { @@ -1245,9 +1282,9 @@ getelfshdr(vnode_t *vp, cred_t *credp, const Ehdr *ehdr, *shstrbasep = kmem_alloc(*shstrsizep, KM_SLEEP); } - if ((err = vn_rdwr(UIO_READ, vp, *shstrbasep, *shstrsizep, + if ((err = vn_rdwr(UIO_READ, vp, *shstrbasep, (ssize_t)*shstrsizep, (offset_t)shdr->sh_offset, UIO_SYSSPACE, 0, (rlim64_t)0, - credp, &resid)) != 0) { + credp, NULL)) != 0) { kmem_free(*shbasep, *shsizep); kmem_free(*shstrbasep, *shstrsizep); return (err); @@ -1262,11 +1299,27 @@ getelfshdr(vnode_t *vp, cred_t *credp, const Ehdr *ehdr, return (0); } +int +elfreadhdr(vnode_t *vp, cred_t *credp, Ehdr *ehdrp, uint_t *nphdrs, + caddr_t *phbasep, size_t *phsizep) +{ + int error; + uint_t nshdrs, shstrndx; + + if ((error = getelfhead(vp, credp, ehdrp, &nshdrs, &shstrndx, + nphdrs)) != 0 || + (error = getelfphdr(vp, credp, ehdrp, *nphdrs, phbasep, + phsizep)) != 0) { + return (error); + } + return (0); +} + static int mapelfexec( vnode_t *vp, Ehdr *ehdr, - int nphdrs, + uint_t nphdrs, caddr_t phdrbase, Phdr **uphdr, Phdr **intphdr, @@ -1276,20 +1329,20 @@ mapelfexec( caddr_t *bssbase, caddr_t *brkbase, intptr_t *voffset, - intptr_t *minaddr, + uintptr_t *minaddrp, size_t len, - long *execsz, + size_t *execsz, size_t *brksize) { Phdr *phdr; - int i, prot, error; + int error, page, prot; caddr_t addr = NULL; - size_t zfodsz; - int ptload = 0; - int page; + caddr_t minaddr = (caddr_t)UINTPTR_MAX; + uint_t i; + size_t zfodsz, memsz; + boolean_t ptload = B_FALSE; off_t offset; - int hsize = ehdr->e_phentsize; - caddr_t mintmp = (caddr_t)-1; + const uint_t hsize = ehdr->e_phentsize; extern int use_brk_lpg; if (ehdr->e_type == ET_DYN) { @@ -1324,14 +1377,12 @@ mapelfexec( } else { *voffset = 0; } + phdr = (Phdr *)phdrbase; for (i = nphdrs; i > 0; i--) { switch (phdr->p_type) { case PT_LOAD: - if ((*intphdr != NULL) && (*uphdr == NULL)) - return (0); - - ptload = 1; + ptload = B_TRUE; prot = PROT_USER; if (phdr->p_flags & PF_R) prot |= PROT_READ; @@ -1342,12 +1393,49 @@ mapelfexec( addr = (caddr_t)((uintptr_t)phdr->p_vaddr + *voffset); + if (*intphdr != NULL && uphdr != NULL && + *uphdr == NULL) { + /* + * The PT_PHDR program header is, strictly + * speaking, optional. If we find that this + * is missing, we will determine the location + * of the program headers based on the address + * of the lowest PT_LOAD segment (namely, this + * one): we subtract the p_offset to get to + * the ELF header and then add back the program + * header offset to get to the program headers. + * We then cons up a Phdr that corresponds to + * the (missing) PT_PHDR, setting the flags + * to 0 to denote that this is artificial and + * should (must) be freed by the caller. + */ + Phdr *cons; + + cons = kmem_zalloc(sizeof (Phdr), KM_SLEEP); + + cons->p_flags = 0; + cons->p_type = PT_PHDR; + cons->p_vaddr = ((uintptr_t)addr - + phdr->p_offset) + ehdr->e_phoff; + + *uphdr = cons; + } + + /* + * The ELF spec dictates that p_filesz may not be + * larger than p_memsz in PT_LOAD segments. + */ + if (phdr->p_filesz > phdr->p_memsz) { + error = EINVAL; + goto bad; + } + /* * Keep track of the segment with the lowest starting * address. */ - if (addr < mintmp) - mintmp = addr; + if (addr < minaddr) + minaddr = addr; zfodsz = (size_t)phdr->p_memsz - phdr->p_filesz; @@ -1367,14 +1455,22 @@ mapelfexec( if (brksize != NULL && use_brk_lpg && zfodsz != 0 && phdr == dataphdrp && (prot & PROT_WRITE)) { - size_t tlen = P2NPHASE((uintptr_t)addr + + const size_t tlen = P2NPHASE((uintptr_t)addr + phdr->p_filesz, PAGESIZE); if (zfodsz > tlen) { + const caddr_t taddr = addr + + phdr->p_filesz + tlen; + + /* + * Since a hole in the AS large enough + * for this object as calculated by + * elfsize() is available, we do not + * need to fear overflow for 'taddr'. + */ curproc->p_brkpageszc = page_szc(map_pgsz(MAPPGSZ_HEAP, - curproc, addr + phdr->p_filesz + - tlen, zfodsz - tlen, 0)); + curproc, taddr, zfodsz - tlen, 0)); } } @@ -1416,7 +1512,12 @@ mapelfexec( *brkbase = addr + phdr->p_memsz; } - *execsz += btopr(phdr->p_memsz); + memsz = btopr(phdr->p_memsz); + if ((*execsz + memsz) < *execsz) { + error = ENOMEM; + goto bad; + } + *execsz += memsz; break; case PT_INTERP: @@ -1430,9 +1531,12 @@ mapelfexec( break; case PT_PHDR: - if (ptload) + if (ptload || phdr->p_flags == 0) goto bad; - *uphdr = phdr; + + if (uphdr != NULL) + *uphdr = phdr; + break; case PT_NULL: @@ -1451,9 +1555,9 @@ mapelfexec( phdr = (Phdr *)((caddr_t)phdr + hsize); } - if (minaddr != NULL) { - ASSERT(mintmp != (caddr_t)-1); - *minaddr = (intptr_t)mintmp; + if (minaddrp != NULL) { + ASSERT(minaddr != (caddr_t)UINTPTR_MAX); + *minaddrp = (uintptr_t)minaddr; } if (brkbase != NULL && secflag_enabled(curproc, PROC_SEC_ASLR)) { @@ -1529,20 +1633,34 @@ elfnote(vnode_t *vp, offset_t *offsetp, int type, int descsz, void *desc, * Copy the section data from one vnode to the section of another vnode. */ static void -copy_scn(Shdr *src, vnode_t *src_vp, Shdr *dst, vnode_t *dst_vp, Off *doffset, - void *buf, size_t size, cred_t *credp, rlim64_t rlimit) +elf_copy_scn(elf_core_ctx_t *ctx, const Shdr *src, vnode_t *src_vp, Shdr *dst) { - ssize_t resid; - size_t len, n = src->sh_size; - offset_t off = 0; + size_t n = src->sh_size; + u_offset_t off = 0; + const u_offset_t soff = src->sh_offset; + const u_offset_t doff = ctx->ecc_doffset; + void *buf = ctx->ecc_buf; + vnode_t *dst_vp = ctx->ecc_vp; + cred_t *credp = ctx->ecc_credp; + + /* Protect the copy loop below from overflow on the offsets */ + if (n > OFF_MAX || (n + soff) > OFF_MAX || (n + doff) > OFF_MAX || + (n + soff) < n || (n + doff) < n) { + dst->sh_size = 0; + dst->sh_offset = 0; + return; + } while (n != 0) { - len = MIN(size, n); - if (vn_rdwr(UIO_READ, src_vp, buf, len, src->sh_offset + off, + const size_t len = MIN(ctx->ecc_bufsz, n); + ssize_t resid; + + if (vn_rdwr(UIO_READ, src_vp, buf, (ssize_t)len, + (offset_t)(soff + off), UIO_SYSSPACE, 0, (rlim64_t)0, credp, &resid) != 0 || - resid >= len || - core_write(dst_vp, UIO_SYSSPACE, *doffset + off, - buf, len - resid, rlimit, credp) != 0) { + resid >= len || resid < 0 || + core_write(dst_vp, UIO_SYSSPACE, (offset_t)(doff + off), + buf, len - resid, ctx->ecc_rlimit, credp) != 0) { dst->sh_size = 0; dst->sh_offset = 0; return; @@ -1554,66 +1672,302 @@ copy_scn(Shdr *src, vnode_t *src_vp, Shdr *dst, vnode_t *dst_vp, Off *doffset, off += len - resid; } - *doffset += src->sh_size; + ctx->ecc_doffset += src->sh_size; } -#ifdef _ELF32_COMPAT -extern size_t elf_datasz_max; -extern size_t elf_zeropg_sz; -#else -size_t elf_datasz_max = 1 * 1024 * 1024; -size_t elf_zeropg_sz = 4 * 1024; -#endif +/* + * Walk sections for a given ELF object, counting (or copying) those of + * interest (CTF, symtab, strtab, .debug_*). + */ +static int +elf_process_obj_scns(elf_core_ctx_t *ctx, vnode_t *mvp, caddr_t saddr, + Shdr *v, uint_t idx, uint_t remain, shstrtab_t *shstrtab, uint_t *countp) +{ + Ehdr ehdr; + const core_content_t content = ctx->ecc_content; + cred_t *credp = ctx->ecc_credp; + Shdr *ctf = NULL, *symtab = NULL, *strtab = NULL; + uintptr_t off = 0; + uint_t nshdrs, shstrndx, nphdrs, count = 0; + u_offset_t *doffp = &ctx->ecc_doffset; + boolean_t ctf_link = B_FALSE; + caddr_t shbase; + size_t shsize, shstrsize; + char *shstrbase; + int error = 0; + const boolean_t justcounting = v == NULL; + + *countp = 0; + + if ((content & + (CC_CONTENT_CTF | CC_CONTENT_SYMTAB | CC_CONTENT_DEBUG)) == 0) { + return (0); + } + + if (getelfhead(mvp, credp, &ehdr, &nshdrs, &shstrndx, &nphdrs) != 0 || + getelfshdr(mvp, credp, &ehdr, nshdrs, shstrndx, &shbase, &shsize, + &shstrbase, &shstrsize) != 0) { + return (0); + } + + /* Starting at index 1 skips SHT_NULL which is expected at index 0 */ + off = ehdr.e_shentsize; + for (uint_t i = 1; i < nshdrs; i++, off += ehdr.e_shentsize) { + Shdr *shdr, *symchk = NULL, *strchk; + const char *name; + + shdr = (Shdr *)(shbase + off); + if (shdr->sh_name >= shstrsize || shdr->sh_type == SHT_NULL) + continue; + + name = shstrbase + shdr->sh_name; + + if (ctf == NULL && + (content & CC_CONTENT_CTF) != 0 && + strcmp(name, shstrtab_data[STR_CTF]) == 0) { + ctf = shdr; + if (ctf->sh_link != 0 && ctf->sh_link < nshdrs) { + /* check linked symtab below */ + symchk = (Shdr *)(shbase + + shdr->sh_link * ehdr.e_shentsize); + ctf_link = B_TRUE; + } else { + continue; + } + } else if (symtab == NULL && + (content & CC_CONTENT_SYMTAB) != 0 && + strcmp(name, shstrtab_data[STR_SYMTAB]) == 0) { + symchk = shdr; + } else if ((content & CC_CONTENT_DEBUG) != 0 && + strncmp(name, ".debug_", strlen(".debug_")) == 0) { + /* + * The design of the above check is intentional. In + * particular, we want to capture any sections that + * begin with '.debug_' for a few reasons: + * + * 1) Various revisions to the DWARF spec end up + * changing the set of section headers that exist. This + * ensures that we don't need to change the kernel to + * get a new version. + * + * 2) Other software uses .debug_ sections for things + * which aren't DWARF. This allows them to be captured + * as well. + */ + count++; + + if (!justcounting) { + if (count > remain) { + error = ENOMEM; + goto done; + } + + elf_ctx_resize_scratch(ctx, shdr->sh_size); + + if (!shstrtab_ndx(shstrtab, + name, &v[idx].sh_name)) { + error = ENOMEM; + goto done; + } + + v[idx].sh_addr = (Addr)(uintptr_t)saddr; + v[idx].sh_type = shdr->sh_type; + v[idx].sh_addralign = shdr->sh_addralign; + *doffp = roundup(*doffp, v[idx].sh_addralign); + v[idx].sh_offset = *doffp; + v[idx].sh_size = shdr->sh_size; + v[idx].sh_link = 0; + v[idx].sh_entsize = shdr->sh_entsize; + v[idx].sh_info = shdr->sh_info; + + elf_copy_scn(ctx, shdr, mvp, &v[idx]); + idx++; + } + + continue; + } else { + continue; + } + + ASSERT(symchk != NULL); + if ((symchk->sh_type != SHT_DYNSYM && + symchk->sh_type != SHT_SYMTAB) || + symchk->sh_link == 0 || symchk->sh_link >= nshdrs) { + ctf_link = B_FALSE; + continue; + } + strchk = (Shdr *)(shbase + symchk->sh_link * ehdr.e_shentsize); + if (strchk->sh_type != SHT_STRTAB) { + ctf_link = B_FALSE; + continue; + } + symtab = symchk; + strtab = strchk; + + if (symtab != NULL && ctf != NULL && + (content & CC_CONTENT_DEBUG) == 0) { + /* No other shdrs are of interest at this point */ + break; + } + } + + if (ctf != NULL) + count += 1; + if (symtab != NULL) + count += 2; + + if (count > remain) { + count = remain; + if (!justcounting) + error = ENOMEM; + goto done; + } + + if (justcounting) + goto done; + + /* output CTF section */ + if (ctf != NULL) { + elf_ctx_resize_scratch(ctx, ctf->sh_size); + + if (!shstrtab_ndx(shstrtab, + shstrtab_data[STR_CTF], &v[idx].sh_name)) { + error = ENOMEM; + goto done; + } + v[idx].sh_addr = (Addr)(uintptr_t)saddr; + v[idx].sh_type = SHT_PROGBITS; + v[idx].sh_addralign = 4; + *doffp = roundup(*doffp, v[idx].sh_addralign); + v[idx].sh_offset = *doffp; + v[idx].sh_size = ctf->sh_size; + + if (ctf_link) { + /* + * The linked symtab (and strtab) will be output + * immediately after this CTF section. Its shdr index + * directly follows this one. + */ + v[idx].sh_link = idx + 1; + ASSERT(symtab != NULL); + } else { + v[idx].sh_link = 0; + } + elf_copy_scn(ctx, ctf, mvp, &v[idx]); + idx++; + } + + /* output SYMTAB/STRTAB sections */ + if (symtab != NULL) { + shstrtype_t symtab_type, strtab_type; + uint_t symtab_name, strtab_name; + + elf_ctx_resize_scratch(ctx, + MAX(symtab->sh_size, strtab->sh_size)); + + if (symtab->sh_type == SHT_DYNSYM) { + symtab_type = STR_DYNSYM; + strtab_type = STR_DYNSTR; + } else { + symtab_type = STR_SYMTAB; + strtab_type = STR_STRTAB; + } + + if (!shstrtab_ndx(shstrtab, + shstrtab_data[symtab_type], &symtab_name)) { + error = ENOMEM; + goto done; + } + if (!shstrtab_ndx(shstrtab, + shstrtab_data[strtab_type], &strtab_name)) { + error = ENOMEM; + goto done; + } + + v[idx].sh_name = symtab_name; + v[idx].sh_type = symtab->sh_type; + v[idx].sh_addr = symtab->sh_addr; + if (ehdr.e_type == ET_DYN || v[idx].sh_addr == 0) + v[idx].sh_addr += (Addr)(uintptr_t)saddr; + v[idx].sh_addralign = symtab->sh_addralign; + *doffp = roundup(*doffp, v[idx].sh_addralign); + v[idx].sh_offset = *doffp; + v[idx].sh_size = symtab->sh_size; + v[idx].sh_link = idx + 1; + v[idx].sh_entsize = symtab->sh_entsize; + v[idx].sh_info = symtab->sh_info; + + elf_copy_scn(ctx, symtab, mvp, &v[idx]); + idx++; + + v[idx].sh_name = strtab_name; + v[idx].sh_type = SHT_STRTAB; + v[idx].sh_flags = SHF_STRINGS; + v[idx].sh_addr = strtab->sh_addr; + if (ehdr.e_type == ET_DYN || v[idx].sh_addr == 0) + v[idx].sh_addr += (Addr)(uintptr_t)saddr; + v[idx].sh_addralign = strtab->sh_addralign; + *doffp = roundup(*doffp, v[idx].sh_addralign); + v[idx].sh_offset = *doffp; + v[idx].sh_size = strtab->sh_size; + + elf_copy_scn(ctx, strtab, mvp, &v[idx]); + idx++; + } + +done: + kmem_free(shstrbase, shstrsize); + kmem_free(shbase, shsize); + + if (error == 0) + *countp = count; + + return (error); +} /* - * This function processes mappings that correspond to load objects to - * examine their respective sections for elfcore(). It's called once with - * v set to NULL to count the number of sections that we're going to need - * and then again with v set to some allocated buffer that we fill in with - * all the section data. + * Walk mappings in process address space, examining those which correspond to + * loaded objects. It is called twice from elfcore: Once to simply count + * relevant sections, and again later to copy those sections once an adequate + * buffer has been allocated for the shdr details. */ static int -process_scns(core_content_t content, proc_t *p, cred_t *credp, vnode_t *vp, - Shdr *v, int nv, rlim64_t rlimit, Off *doffsetp, int *nshdrsp) +elf_process_scns(elf_core_ctx_t *ctx, Shdr *v, uint_t nv, uint_t *nshdrsp) { vnode_t *lastvp = NULL; struct seg *seg; - int i, j; - void *data = NULL; - size_t datasz = 0; + uint_t idx = 0, remain; shstrtab_t shstrtab; - struct as *as = p->p_as; + struct as *as = ctx->ecc_p->p_as; int error = 0; - if (!shstrtab_init(&shstrtab)) { - error = ENOMEM; - goto done; + ASSERT(AS_WRITE_HELD(as)); + + if (v != NULL) { + ASSERT(nv != 0); + + if (!shstrtab_init(&shstrtab)) + return (ENOMEM); + remain = nv; + } else { + ASSERT(nv == 0); + + /* + * The shdrs are being counted, rather than outputting them + * into a buffer. Leave room for two entries: the SHT_NULL at + * index 0 and the shstrtab at the end. + */ + remain = UINT_MAX - 2; } - i = 1; + /* Per the ELF spec, shdr index 0 is reserved. */ + idx = 1; for (seg = AS_SEGFIRST(as); seg != NULL; seg = AS_SEGNEXT(as, seg)) { - uint_t prot; vnode_t *mvp; void *tmp = NULL; - caddr_t saddr = seg->s_base; - caddr_t naddr; - caddr_t eaddr; + caddr_t saddr = seg->s_base, naddr, eaddr; size_t segsize; - - Ehdr ehdr; - int nshdrs, shstrndx, nphdrs; - caddr_t shbase; - ssize_t shsize; - char *shstrbase; - ssize_t shstrsize; - - Shdr *shdr; - const char *name; - size_t sz; - uintptr_t off; - - int ctf_ndx = 0; - int symtab_ndx = 0; + uint_t count, prot; /* * Since we're just looking for text segments of load @@ -1639,301 +1993,61 @@ process_scns(core_content_t content, proc_t *p, cred_t *credp, vnode_t *vp, if ((prot & (PROT_WRITE | PROT_EXEC)) != PROT_EXEC) continue; - if (getelfhead(mvp, credp, &ehdr, &nshdrs, &shstrndx, - &nphdrs) != 0 || - getelfshdr(mvp, credp, &ehdr, nshdrs, shstrndx, - &shbase, &shsize, &shstrbase, &shstrsize) != 0) - continue; - - off = ehdr.e_shentsize; - for (j = 1; j < nshdrs; j++, off += ehdr.e_shentsize) { - Shdr *symtab = NULL, *strtab; - size_t allocsz; - - shdr = (Shdr *)(shbase + off); - allocsz = MIN(shdr->sh_size, elf_datasz_max); - - if (shdr->sh_name >= shstrsize) - continue; - - name = shstrbase + shdr->sh_name; - - if (strcmp(name, shstrtab_data[STR_CTF]) == 0) { - if ((content & CC_CONTENT_CTF) == 0 || - ctf_ndx != 0) - continue; - - if (shdr->sh_link > 0 && - shdr->sh_link < nshdrs) { - symtab = (Shdr *)(shbase + - shdr->sh_link * ehdr.e_shentsize); - } - - if (v != NULL && i < nv - 1) { - if (allocsz > datasz) { - if (data != NULL) - kmem_free(data, datasz); - - datasz = allocsz; - data = kmem_alloc(datasz, - KM_SLEEP); - } - - if (!shstrtab_ndx(&shstrtab, - shstrtab_data[STR_CTF], - &v[i].sh_name)) { - error = ENOMEM; - goto done; - } - v[i].sh_addr = (Addr)(uintptr_t)saddr; - v[i].sh_type = SHT_PROGBITS; - v[i].sh_addralign = 4; - *doffsetp = roundup(*doffsetp, - v[i].sh_addralign); - v[i].sh_offset = *doffsetp; - v[i].sh_size = shdr->sh_size; - if (symtab == NULL) { - v[i].sh_link = 0; - } else if (symtab->sh_type == - SHT_SYMTAB && - symtab_ndx != 0) { - v[i].sh_link = - symtab_ndx; - } else { - v[i].sh_link = i + 1; - } - - copy_scn(shdr, mvp, &v[i], vp, - doffsetp, data, datasz, credp, - rlimit); - } - - ctf_ndx = i++; - - /* - * We've already dumped the symtab. - */ - if (symtab != NULL && - symtab->sh_type == SHT_SYMTAB && - symtab_ndx != 0) - continue; - - } else if (strcmp(name, - shstrtab_data[STR_SYMTAB]) == 0) { - if ((content & CC_CONTENT_SYMTAB) == 0 || - symtab != 0) - continue; - - symtab = shdr; - } else if (strncmp(name, ".debug_", - strlen(".debug_")) == 0) { - /* - * The design of the above check is intentional. - * In particular, we want to capture any - * sections that begin with '.debug_' for a few - * reasons: - * - * 1) Various revisions to the DWARF spec end up - * changing the set of section headers that - * exist. This ensures that we don't need to - * change the kernel to get a new version. - * - * 2) Other software uses .debug_ sections for - * things which aren't DWARF. This allows them - * to be captured as well. - */ - if ((content & CC_CONTENT_DEBUG) == 0) - continue; - - if (v != NULL && i < nv - 1) { - if (allocsz > datasz) { - if (data != NULL) - kmem_free(data, datasz); - - datasz = allocsz; - data = kmem_alloc(datasz, - KM_SLEEP); - } - - if (!shstrtab_ndx(&shstrtab, - name, &v[i].sh_name)) { - error = ENOMEM; - goto done; - } - v[i].sh_addr = (Addr)(uintptr_t)saddr; - v[i].sh_type = shdr->sh_type; - v[i].sh_addralign = shdr->sh_addralign; - *doffsetp = roundup(*doffsetp, - v[i].sh_addralign); - v[i].sh_offset = *doffsetp; - v[i].sh_size = shdr->sh_size; - v[i].sh_link = 0; - v[i].sh_entsize = shdr->sh_entsize; - v[i].sh_info = shdr->sh_info; - - copy_scn(shdr, mvp, &v[i], vp, - doffsetp, data, datasz, credp, - rlimit); - } - - i++; - continue; - } - - if (symtab != NULL) { - if ((symtab->sh_type != SHT_DYNSYM && - symtab->sh_type != SHT_SYMTAB) || - symtab->sh_link == 0 || - symtab->sh_link >= nshdrs) - continue; - - strtab = (Shdr *)(shbase + - symtab->sh_link * ehdr.e_shentsize); - - if (strtab->sh_type != SHT_STRTAB) - continue; - - if (v != NULL && i < nv - 2) { - sz = MAX(symtab->sh_size, - strtab->sh_size); - allocsz = MIN(sz, elf_datasz_max); - if (allocsz > datasz) { - if (data != NULL) - kmem_free(data, datasz); - - datasz = allocsz; - data = kmem_alloc(datasz, - KM_SLEEP); - } - - if (symtab->sh_type == SHT_DYNSYM) { - if (!shstrtab_ndx(&shstrtab, - shstrtab_data[STR_DYNSYM], - &v[i].sh_name)) { - error = ENOMEM; - goto done; - } - if (!shstrtab_ndx(&shstrtab, - shstrtab_data[STR_DYNSTR], - &v[i + 1].sh_name)) { - error = ENOMEM; - goto done; - } - } else { - if (!shstrtab_ndx(&shstrtab, - shstrtab_data[STR_SYMTAB], - &v[i].sh_name)) { - error = ENOMEM; - goto done; - } - if (!shstrtab_ndx(&shstrtab, - shstrtab_data[STR_STRTAB], - &v[i + 1].sh_name)) { - error = ENOMEM; - goto done; - } - } - - v[i].sh_type = symtab->sh_type; - v[i].sh_addr = symtab->sh_addr; - if (ehdr.e_type == ET_DYN || - v[i].sh_addr == 0) - v[i].sh_addr += - (Addr)(uintptr_t)saddr; - v[i].sh_addralign = - symtab->sh_addralign; - *doffsetp = roundup(*doffsetp, - v[i].sh_addralign); - v[i].sh_offset = *doffsetp; - v[i].sh_size = symtab->sh_size; - v[i].sh_link = i + 1; - v[i].sh_entsize = symtab->sh_entsize; - v[i].sh_info = symtab->sh_info; - - copy_scn(symtab, mvp, &v[i], vp, - doffsetp, data, datasz, credp, - rlimit); - - v[i + 1].sh_type = SHT_STRTAB; - v[i + 1].sh_flags = SHF_STRINGS; - v[i + 1].sh_addr = symtab->sh_addr; - if (ehdr.e_type == ET_DYN || - v[i + 1].sh_addr == 0) - v[i + 1].sh_addr += - (Addr)(uintptr_t)saddr; - v[i + 1].sh_addralign = - strtab->sh_addralign; - *doffsetp = roundup(*doffsetp, - v[i + 1].sh_addralign); - v[i + 1].sh_offset = *doffsetp; - v[i + 1].sh_size = strtab->sh_size; - - copy_scn(strtab, mvp, &v[i + 1], vp, - doffsetp, data, datasz, credp, - rlimit); - } - - if (symtab->sh_type == SHT_SYMTAB) - symtab_ndx = i; - i += 2; - } - } + error = elf_process_obj_scns(ctx, mvp, saddr, v, idx, remain, + &shstrtab, &count); + if (error != 0) + goto done; - kmem_free(shstrbase, shstrsize); - kmem_free(shbase, shsize); + ASSERT(count <= remain); + ASSERT(v == NULL || (idx + count) < nv); + remain -= count; + idx += count; lastvp = mvp; } if (v == NULL) { - if (i == 1) + if (idx == 1) { *nshdrsp = 0; - else - *nshdrsp = i + 1; - goto done; + } else { + /* Include room for the shrstrtab at the end */ + *nshdrsp = idx + 1; + } + return (0); } - if (i != nv - 1) { + if (idx != nv - 1) { cmn_err(CE_WARN, "elfcore: core dump failed for " - "process %d; address space is changing", p->p_pid); + "process %d; address space is changing", + ctx->ecc_p->p_pid); error = EIO; goto done; } if (!shstrtab_ndx(&shstrtab, shstrtab_data[STR_SHSTRTAB], - &v[i].sh_name)) { + &v[idx].sh_name)) { error = ENOMEM; goto done; } - v[i].sh_size = shstrtab_size(&shstrtab); - v[i].sh_addralign = 1; - *doffsetp = roundup(*doffsetp, v[i].sh_addralign); - v[i].sh_offset = *doffsetp; - v[i].sh_flags = SHF_STRINGS; - v[i].sh_type = SHT_STRTAB; - - if (v[i].sh_size > datasz) { - if (data != NULL) - kmem_free(data, datasz); - - datasz = v[i].sh_size; - data = kmem_alloc(datasz, - KM_SLEEP); + v[idx].sh_size = shstrtab_size(&shstrtab); + v[idx].sh_addralign = 1; + v[idx].sh_offset = ctx->ecc_doffset; + v[idx].sh_flags = SHF_STRINGS; + v[idx].sh_type = SHT_STRTAB; + + elf_ctx_resize_scratch(ctx, v[idx].sh_size); + VERIFY3U(ctx->ecc_bufsz, >=, v[idx].sh_size); + shstrtab_dump(&shstrtab, ctx->ecc_buf); + + error = core_write(ctx->ecc_vp, UIO_SYSSPACE, ctx->ecc_doffset, + ctx->ecc_buf, v[idx].sh_size, ctx->ecc_rlimit, ctx->ecc_credp); + if (error == 0) { + ctx->ecc_doffset += v[idx].sh_size; } - shstrtab_dump(&shstrtab, data); - - if ((error = core_write(vp, UIO_SYSSPACE, *doffsetp, - data, v[i].sh_size, rlimit, credp)) != 0) - goto done; - - *doffsetp += v[i].sh_size; - done: - if (data != NULL) - kmem_free(data, datasz); - - shstrtab_fini(&shstrtab); + if (v != NULL) + shstrtab_fini(&shstrtab); return (error); } @@ -1942,28 +2056,30 @@ int elfcore(vnode_t *vp, proc_t *p, cred_t *credp, rlim64_t rlimit, int sig, core_content_t content) { - offset_t poffset, soffset; - Off doffset; - int error, i, nphdrs, nshdrs; - int overflow = 0; + u_offset_t poffset, soffset, doffset; + int error; + uint_t i, nphdrs, nshdrs; struct seg *seg; struct as *as = p->p_as; - union { - Ehdr ehdr; - Phdr phdr[1]; - Shdr shdr[1]; - } *bigwad; - size_t bigsize; - size_t phdrsz, shdrsz; + void *bigwad, *zeropg = NULL; + size_t bigsize, phdrsz, shdrsz; Ehdr *ehdr; - Phdr *v; - void *zeropg = NULL; - caddr_t brkbase; - size_t brksize; - caddr_t stkbase; - size_t stksize; - int ntries = 0; + Phdr *phdr; + Shdr shdr0; + caddr_t brkbase, stkbase; + size_t brksize, stksize; + boolean_t overflowed = B_FALSE, retried = B_FALSE; klwp_t *lwp = ttolwp(curthread); + elf_core_ctx_t ctx = { + .ecc_vp = vp, + .ecc_p = p, + .ecc_credp = credp, + .ecc_rlimit = rlimit, + .ecc_content = content, + .ecc_doffset = 0, + .ecc_buf = NULL, + .ecc_bufsz = 0 + }; top: /* @@ -1981,28 +2097,31 @@ top: */ nshdrs = 0; if (content & (CC_CONTENT_CTF | CC_CONTENT_SYMTAB | CC_CONTENT_DEBUG)) { - (void) process_scns(content, p, credp, NULL, NULL, 0, 0, - NULL, &nshdrs); + VERIFY0(elf_process_scns(&ctx, NULL, 0, &nshdrs)); } AS_LOCK_EXIT(as); - ASSERT(nshdrs == 0 || nshdrs > 1); - /* - * The core file contents may required zero section headers, but if + * The core file contents may require zero section headers, but if * we overflow the 16 bits allotted to the program header count in * the ELF header, we'll need that program header at index zero. */ if (nshdrs == 0 && nphdrs >= PN_XNUM) nshdrs = 1; + /* + * Allocate a buffer which is sized adequately to hold the ehdr, phdrs + * or shdrs needed to produce the core file. It is used for the three + * tasks sequentially, not simultaneously, so it does not need space + * for all three data at once, only the largest one. + */ + VERIFY(nphdrs >= 2); phdrsz = nphdrs * sizeof (Phdr); shdrsz = nshdrs * sizeof (Shdr); - - bigsize = MAX(sizeof (*bigwad), MAX(phdrsz, shdrsz)); + bigsize = MAX(sizeof (Ehdr), MAX(phdrsz, shdrsz)); bigwad = kmem_alloc(bigsize, KM_SLEEP); - ehdr = &bigwad->ehdr; + ehdr = (Ehdr *)bigwad; bzero(ehdr, sizeof (*ehdr)); ehdr->e_ident[EI_MAG0] = ELFMAG0; @@ -2038,6 +2157,11 @@ top: #endif /* !defined(_LP64) || defined(_ELF32_COMPAT) */ + poffset = sizeof (Ehdr); + soffset = sizeof (Ehdr) + phdrsz; + doffset = sizeof (Ehdr) + phdrsz + shdrsz; + bzero(&shdr0, sizeof (shdr0)); + /* * If the count of program headers or section headers or the index * of the section string table can't fit in the mere 16 bits @@ -2045,51 +2169,53 @@ top: * extended formats and put the real values in the section header * as index 0. */ - ehdr->e_ident[EI_VERSION] = EV_CURRENT; - ehdr->e_version = EV_CURRENT; - ehdr->e_ehsize = sizeof (Ehdr); - - if (nphdrs >= PN_XNUM) + if (nphdrs >= PN_XNUM) { ehdr->e_phnum = PN_XNUM; - else + shdr0.sh_info = nphdrs; + } else { ehdr->e_phnum = (unsigned short)nphdrs; - - ehdr->e_phoff = sizeof (Ehdr); - ehdr->e_phentsize = sizeof (Phdr); + } if (nshdrs > 0) { - if (nshdrs >= SHN_LORESERVE) + if (nshdrs >= SHN_LORESERVE) { ehdr->e_shnum = 0; - else + shdr0.sh_size = nshdrs; + } else { ehdr->e_shnum = (unsigned short)nshdrs; + } - if (nshdrs - 1 >= SHN_LORESERVE) + if (nshdrs - 1 >= SHN_LORESERVE) { ehdr->e_shstrndx = SHN_XINDEX; - else + shdr0.sh_link = nshdrs - 1; + } else { ehdr->e_shstrndx = (unsigned short)(nshdrs - 1); + } - ehdr->e_shoff = ehdr->e_phoff + ehdr->e_phentsize * nphdrs; + ehdr->e_shoff = soffset; ehdr->e_shentsize = sizeof (Shdr); } + ehdr->e_ident[EI_VERSION] = EV_CURRENT; + ehdr->e_version = EV_CURRENT; + ehdr->e_ehsize = sizeof (Ehdr); + ehdr->e_phoff = poffset; + ehdr->e_phentsize = sizeof (Phdr); + if (error = core_write(vp, UIO_SYSSPACE, (offset_t)0, ehdr, - sizeof (Ehdr), rlimit, credp)) + sizeof (Ehdr), rlimit, credp)) { goto done; + } - poffset = sizeof (Ehdr); - soffset = sizeof (Ehdr) + phdrsz; - doffset = sizeof (Ehdr) + phdrsz + shdrsz; - - v = &bigwad->phdr[0]; - bzero(v, phdrsz); + phdr = (Phdr *)bigwad; + bzero(phdr, phdrsz); - setup_old_note_header(&v[0], p); - v[0].p_offset = doffset = roundup(doffset, sizeof (Word)); - doffset += v[0].p_filesz; + setup_old_note_header(&phdr[0], p); + phdr[0].p_offset = doffset = roundup(doffset, sizeof (Word)); + doffset += phdr[0].p_filesz; - setup_note_header(&v[1], p); - v[1].p_offset = doffset = roundup(doffset, sizeof (Word)); - doffset += v[1].p_filesz; + setup_note_header(&phdr[1], p); + phdr[1].p_offset = doffset = roundup(doffset, sizeof (Word)); + doffset += phdr[1].p_filesz; mutex_enter(&p->p_lock); @@ -2121,21 +2247,23 @@ top: prot = pr_getprot(seg, 0, &tmp, &saddr, &naddr, eaddr); prot &= PROT_READ | PROT_WRITE | PROT_EXEC; - if ((size = (size_t)(naddr - saddr)) == 0) - continue; - if (i == nphdrs) { - overflow++; + if ((size = (size_t)(naddr - saddr)) == 0) { + ASSERT(tmp == NULL); continue; + } else if (i == nphdrs) { + pr_getprot_done(&tmp); + overflowed = B_TRUE; + break; } - v[i].p_type = PT_LOAD; - v[i].p_vaddr = (Addr)(uintptr_t)saddr; - v[i].p_memsz = size; + phdr[i].p_type = PT_LOAD; + phdr[i].p_vaddr = (Addr)(uintptr_t)saddr; + phdr[i].p_memsz = size; if (prot & PROT_READ) - v[i].p_flags |= PF_R; + phdr[i].p_flags |= PF_R; if (prot & PROT_WRITE) - v[i].p_flags |= PF_W; + phdr[i].p_flags |= PF_W; if (prot & PROT_EXEC) - v[i].p_flags |= PF_X; + phdr[i].p_flags |= PF_X; /* * Figure out which mappings to include in the core. @@ -2197,20 +2325,23 @@ top: } doffset = roundup(doffset, sizeof (Word)); - v[i].p_offset = doffset; - v[i].p_filesz = size; + phdr[i].p_offset = doffset; + phdr[i].p_filesz = size; doffset += size; exclude: i++; } - ASSERT(tmp == NULL); + VERIFY(tmp == NULL); + if (overflowed) + break; } AS_LOCK_EXIT(as); - if (overflow || i != nphdrs) { - if (ntries++ == 0) { + if (overflowed || i != nphdrs) { + if (!retried) { + retried = B_TRUE; + overflowed = B_FALSE; kmem_free(bigwad, bigsize); - overflow = 0; goto top; } cmn_err(CE_WARN, "elfcore: core dump failed for " @@ -2220,23 +2351,25 @@ exclude: } if ((error = core_write(vp, UIO_SYSSPACE, poffset, - v, phdrsz, rlimit, credp)) != 0) + phdr, phdrsz, rlimit, credp)) != 0) { goto done; + } - if ((error = write_old_elfnotes(p, sig, vp, v[0].p_offset, rlimit, - credp)) != 0) + if ((error = write_old_elfnotes(p, sig, vp, phdr[0].p_offset, rlimit, + credp)) != 0) { goto done; - - if ((error = write_elfnotes(p, sig, vp, v[1].p_offset, rlimit, - credp, content)) != 0) + } + if ((error = write_elfnotes(p, sig, vp, phdr[1].p_offset, rlimit, + credp, content)) != 0) { goto done; + } for (i = 2; i < nphdrs; i++) { prkillinfo_t killinfo; sigqueue_t *sq; int sig, j; - if (v[i].p_filesz == 0) + if (phdr[i].p_filesz == 0) continue; /* @@ -2254,8 +2387,8 @@ exclude: * this from mappings that were excluded due to the core file * content settings. */ - if ((v[i].p_flags & (PF_R | PF_W | PF_X)) == 0) { - size_t towrite = v[i].p_filesz; + if ((phdr[i].p_flags & (PF_R | PF_W | PF_X)) == 0) { + size_t towrite = phdr[i].p_filesz; size_t curoff = 0; if (zeropg == NULL) { @@ -2267,24 +2400,21 @@ exclude: size_t len = MIN(towrite, elf_zeropg_sz); error = core_write(vp, UIO_SYSSPACE, - v[i].p_offset + curoff, zeropg, len, rlimit, - credp); + phdr[i].p_offset + curoff, zeropg, len, + rlimit, credp); if (error != 0) break; towrite -= len; curoff += len; } - - if (error == 0) - continue; } else { - error = core_seg(p, vp, v[i].p_offset, - (caddr_t)(uintptr_t)v[i].p_vaddr, v[i].p_filesz, - rlimit, credp); - if (error == 0) - continue; + error = core_seg(p, vp, phdr[i].p_offset, + (caddr_t)(uintptr_t)phdr[i].p_vaddr, + phdr[i].p_filesz, rlimit, credp); } + if (error == 0) + continue; if ((sig = lwp->lwp_cursig) == 0) { /* @@ -2294,14 +2424,14 @@ exclude: * bytes. This undocumented interface will let us * understand the nature of the failure. */ - (void) core_write(vp, UIO_SYSSPACE, v[i].p_offset, + (void) core_write(vp, UIO_SYSSPACE, phdr[i].p_offset, &error, sizeof (error), rlimit, credp); - v[i].p_filesz = 0; - v[i].p_flags |= PF_SUNW_FAILURE; + phdr[i].p_filesz = 0; + phdr[i].p_flags |= PF_SUNW_FAILURE; if ((error = core_write(vp, UIO_SYSSPACE, - poffset + sizeof (v[i]) * i, &v[i], sizeof (v[i]), - rlimit, credp)) != 0) + poffset + sizeof (Phdr) * i, &phdr[i], + sizeof (Phdr), rlimit, credp)) != 0) goto done; continue; @@ -2343,15 +2473,15 @@ exclude: } #endif - (void) core_write(vp, UIO_SYSSPACE, v[i].p_offset, + (void) core_write(vp, UIO_SYSSPACE, phdr[i].p_offset, &killinfo, sizeof (killinfo), rlimit, credp); /* * For the segment on which we took the signal, indicate that * its data now refers to a siginfo. */ - v[i].p_filesz = 0; - v[i].p_flags |= PF_SUNW_FAILURE | PF_SUNW_KILLED | + phdr[i].p_filesz = 0; + phdr[i].p_flags |= PF_SUNW_FAILURE | PF_SUNW_KILLED | PF_SUNW_SIGINFO; /* @@ -2359,53 +2489,47 @@ exclude: * is due to a signal. */ for (j = i + 1; j < nphdrs; j++) { - v[j].p_filesz = 0; - v[j].p_flags |= PF_SUNW_FAILURE | PF_SUNW_KILLED; + phdr[j].p_filesz = 0; + phdr[j].p_flags |= PF_SUNW_FAILURE | PF_SUNW_KILLED; } /* * Finally, write out our modified program headers. */ if ((error = core_write(vp, UIO_SYSSPACE, - poffset + sizeof (v[i]) * i, &v[i], - sizeof (v[i]) * (nphdrs - i), rlimit, credp)) != 0) + poffset + sizeof (Phdr) * i, &phdr[i], + sizeof (Phdr) * (nphdrs - i), rlimit, credp)) != 0) { goto done; + } break; } if (nshdrs > 0) { - bzero(&bigwad->shdr[0], shdrsz); - - if (nshdrs >= SHN_LORESERVE) - bigwad->shdr[0].sh_size = nshdrs; - - if (nshdrs - 1 >= SHN_LORESERVE) - bigwad->shdr[0].sh_link = nshdrs - 1; - - if (nphdrs >= PN_XNUM) - bigwad->shdr[0].sh_info = nphdrs; + Shdr *shdr = (Shdr *)bigwad; + bzero(shdr, shdrsz); if (nshdrs > 1) { + ctx.ecc_doffset = doffset; AS_LOCK_ENTER(as, RW_WRITER); - if ((error = process_scns(content, p, credp, vp, - &bigwad->shdr[0], nshdrs, rlimit, &doffset, - NULL)) != 0) { - AS_LOCK_EXIT(as); + error = elf_process_scns(&ctx, shdr, nshdrs, NULL); + AS_LOCK_EXIT(as); + if (error != 0) { goto done; } - AS_LOCK_EXIT(as); } + /* Copy any extended format data destined for the first shdr */ + bcopy(&shdr0, shdr, sizeof (shdr0)); - if ((error = core_write(vp, UIO_SYSSPACE, soffset, - &bigwad->shdr[0], shdrsz, rlimit, credp)) != 0) - goto done; + error = core_write(vp, UIO_SYSSPACE, soffset, shdr, shdrsz, + rlimit, credp); } done: - if (zeropg != NULL) { + if (zeropg != NULL) kmem_free(zeropg, elf_zeropg_sz); - } + if (ctx.ecc_bufsz != 0) + kmem_free(ctx.ecc_buf, ctx.ecc_bufsz); kmem_free(bigwad, bigsize); return (error); } @@ -2430,7 +2554,7 @@ static struct modlexec modlexec = { #ifdef _LP64 extern int elf32exec(vnode_t *vp, execa_t *uap, uarg_t *args, - intpdata_t *idatap, int level, long *execsz, + intpdata_t *idatap, int level, size_t *execsz, int setid, caddr_t exec_file, cred_t *cred, int brand_action); extern int elf32core(vnode_t *vp, proc_t *p, cred_t *credp, diff --git a/usr/src/uts/common/exec/elf/elf_impl.h b/usr/src/uts/common/exec/elf/elf_impl.h index 010d5e6256..504cf84dd2 100644 --- a/usr/src/uts/common/exec/elf/elf_impl.h +++ b/usr/src/uts/common/exec/elf/elf_impl.h @@ -22,12 +22,13 @@ * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright 2019 Joyent, Inc. + */ #ifndef _ELF_ELF_IMPL_H #define _ELF_ELF_IMPL_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -71,6 +72,17 @@ typedef struct { char name[8]; } Note; +typedef struct { + vnode_t *ecc_vp; + proc_t *ecc_p; + cred_t *ecc_credp; + rlim64_t ecc_rlimit; + core_content_t ecc_content; + u_offset_t ecc_doffset; + void *ecc_buf; + size_t ecc_bufsz; +} elf_core_ctx_t; + #ifdef _ELF32_COMPAT /* * These are defined only for the 32-bit compatibility @@ -79,6 +91,7 @@ typedef struct { #define elfexec elf32exec #define elfnote elf32note #define elfcore elf32core +#define elfreadhdr elf32readhdr #define mapexec_brand mapexec32_brand #define setup_note_header setup_note_header32 #define write_elfnotes write_elfnotes32 diff --git a/usr/src/uts/common/exec/intp/intp.c b/usr/src/uts/common/exec/intp/intp.c index 269ba86b1b..935bad0a8c 100644 --- a/usr/src/uts/common/exec/intp/intp.c +++ b/usr/src/uts/common/exec/intp/intp.c @@ -22,10 +22,11 @@ * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2012 Milan Jurik. All rights reserved. + * Copyright 2019 Joyent, Inc. */ /* Copyright (c) 1988 AT&T */ -/* All Rights Reserved */ +/* All Rights Reserved */ /* from S5R4 1.6 */ @@ -54,7 +55,7 @@ #include <sys/modctl.h> extern int intpexec(struct vnode *, struct execa *, struct uarg *, - struct intpdata *, int, long *, int, caddr_t, struct cred *, int); + struct intpdata *, int, size_t *, int, caddr_t, struct cred *, int); static struct execsw esw = { intpmagicstr, @@ -132,7 +133,7 @@ getintphead(struct vnode *vp, struct intpdata *idatap) * arguments provided on the command line. Thus, for * example, you can say * - * #! /usr/bin/awk -f + * #! /usr/bin/awk -f */ for (cp = &linep[2]; *cp == ' '; cp++) ; @@ -184,7 +185,7 @@ intpexec( struct uarg *args, struct intpdata *idatap, int level, - long *execsz, + size_t *execsz, int setid, caddr_t exec_file, struct cred *cred, diff --git a/usr/src/uts/common/exec/java/java.c b/usr/src/uts/common/exec/java/java.c index 575b19a13c..9b38dec5a0 100644 --- a/usr/src/uts/common/exec/java/java.c +++ b/usr/src/uts/common/exec/java/java.c @@ -21,6 +21,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2019 Joyent, Inc. */ /* @@ -84,7 +85,7 @@ char *jexec_arg = "-jar"; /*ARGSUSED3*/ static int javaexec(vnode_t *vp, struct execa *uap, struct uarg *args, - struct intpdata *idatap, int level, long *execsz, int setid, + struct intpdata *idatap, int level, size_t *execsz, int setid, caddr_t execfile, cred_t *cred, int brand_action) { struct intpdata idata; diff --git a/usr/src/uts/common/exec/shbin/shbin.c b/usr/src/uts/common/exec/shbin/shbin.c index 80300b504f..25a88b05c1 100644 --- a/usr/src/uts/common/exec/shbin/shbin.c +++ b/usr/src/uts/common/exec/shbin/shbin.c @@ -22,6 +22,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2019 Joyent, Inc. */ #include <sys/types.h> @@ -54,7 +55,7 @@ shbinexec( struct uarg *args, struct intpdata *idatap, int level, - long *execsz, + size_t *execsz, int setid, caddr_t exec_file, struct cred *cred, @@ -156,7 +157,7 @@ shbinexec( struct uarg *args, struct intpdata *idatap, int level, - long *execsz, + size_t *execsz, int setid, caddr_t exec_file, struct cred *cred, diff --git a/usr/src/uts/common/fs/proc/prioctl.c b/usr/src/uts/common/fs/proc/prioctl.c index 89b68f04d5..d596d06a34 100644 --- a/usr/src/uts/common/fs/proc/prioctl.c +++ b/usr/src/uts/common/fs/proc/prioctl.c @@ -22,7 +22,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. - * Copyright 2017 Joyent, Inc. + * Copyright 2019 Joyent, Inc. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ @@ -945,7 +945,7 @@ startover: case PIOCNMAP: /* get number of memory mappings */ { - int n; + uint_t n; struct as *as = p->p_as; if ((p->p_flag & SSYS) || as == &kas) @@ -958,7 +958,7 @@ startover: mutex_enter(&p->p_lock); } prunlock(pnp); - if (copyout(&n, cmaddr, sizeof (int))) + if (copyout(&n, cmaddr, sizeof (uint_t))) error = EFAULT; break; } @@ -2564,7 +2564,7 @@ startover: case PIOCNMAP: /* get number of memory mappings */ { - int n; + uint_t n; struct as *as = p->p_as; if ((p->p_flag & SSYS) || as == &kas) @@ -2577,7 +2577,7 @@ startover: mutex_enter(&p->p_lock); } prunlock(pnp); - if (copyout(&n, cmaddr, sizeof (int))) + if (copyout(&n, cmaddr, sizeof (uint_t))) error = EFAULT; break; } diff --git a/usr/src/uts/common/fs/proc/prsubr.c b/usr/src/uts/common/fs/proc/prsubr.c index 021d2b4b49..da43f59092 100644 --- a/usr/src/uts/common/fs/proc/prsubr.c +++ b/usr/src/uts/common/fs/proc/prsubr.c @@ -21,7 +21,7 @@ /* * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2017, Joyent, Inc. + * Copyright 2019 Joyent, Inc. * Copyright 2020 OmniOS Community Edition (OmniOSce) Association. * Copyright 2022 MNX Cloud, Inc. */ @@ -1409,10 +1409,10 @@ prgetaction32(proc_t *p, user_t *up, uint_t sig, struct sigaction32 *sp) /* * Count the number of segments in this process's address space. */ -int +uint_t prnsegs(struct as *as, int reserved) { - int n = 0; + uint_t n = 0; struct seg *seg; ASSERT(as != &kas && AS_WRITE_HELD(as)); @@ -1429,8 +1429,21 @@ prnsegs(struct as *as, int reserved) for (saddr = seg->s_base; saddr < eaddr; saddr = naddr) { (void) pr_getprot(seg, reserved, &tmp, &saddr, &naddr, eaddr); - if (saddr != naddr) + if (saddr != naddr) { n++; + /* + * prnsegs() was formerly designated to return + * an 'int' despite having no ability or use + * for negative results. As part of changing + * it to 'uint_t', keep the old effective limit + * of INT_MAX in place. + */ + if (n == INT_MAX) { + pr_getprot_done(&tmp); + ASSERT(tmp == NULL); + return (n); + } + } } ASSERT(tmp == NULL); diff --git a/usr/src/uts/common/os/brand.c b/usr/src/uts/common/os/brand.c index 8b3177b916..68b699630a 100644 --- a/usr/src/uts/common/os/brand.c +++ b/usr/src/uts/common/os/brand.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2019 Joyent, Inc. */ #include <sys/kmem.h> @@ -600,7 +601,7 @@ restoreexecenv(struct execenv *ep, stack_t *sp) /*ARGSUSED*/ int brand_solaris_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, - intpdata_t *idatap, int level, long *execsz, int setid, caddr_t exec_file, + intpdata_t *idatap, int level, size_t *execsz, int setid, caddr_t exec_file, cred_t *cred, int brand_action, struct brand *pbrand, char *bname, char *brandlib, char *brandlib32, char *brandlinker, char *brandlinker32) { diff --git a/usr/src/uts/common/os/core.c b/usr/src/uts/common/os/core.c index d5e272c16a..d56484ac34 100644 --- a/usr/src/uts/common/os/core.c +++ b/usr/src/uts/common/os/core.c @@ -21,12 +21,12 @@ /* * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011, Joyent Inc. All rights reserved. * Copyright (c) 2016 by Delphix. All rights reserved. + * Copyright 2019 Joyent Inc. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ -/* All Rights Reserved */ +/* All Rights Reserved */ #include <sys/param.h> #include <sys/types.h> @@ -793,7 +793,7 @@ clock_t core_delay_usec = 10000; * using core_write() below, and so it has the same failure semantics. */ int -core_seg(proc_t *p, vnode_t *vp, offset_t offset, caddr_t addr, size_t size, +core_seg(proc_t *p, vnode_t *vp, u_offset_t offset, caddr_t addr, size_t size, rlim64_t rlimit, cred_t *credp) { caddr_t eaddr; @@ -801,6 +801,11 @@ core_seg(proc_t *p, vnode_t *vp, offset_t offset, caddr_t addr, size_t size, size_t len; int err = 0; + if (offset > OFF_MAX || offset + size > OFF_MAX || + offset + size < offset) { + return (EOVERFLOW); + } + eaddr = addr + size; for (base = addr; base < eaddr; base += len) { len = eaddr - base; @@ -841,15 +846,20 @@ core_seg(proc_t *p, vnode_t *vp, offset_t offset, caddr_t addr, size_t size, * unexpectedly returns zero but no progress has been made, we return ENOSPC. */ int -core_write(vnode_t *vp, enum uio_seg segflg, offset_t offset, +core_write(vnode_t *vp, enum uio_seg segflg, u_offset_t offset, const void *buf, size_t len, rlim64_t rlimit, cred_t *credp) { ssize_t resid = len; int error = 0; + if (offset > OFF_MAX || offset + len > OFF_MAX || + offset + len < offset) { + return (EOVERFLOW); + } + while (len != 0) { - error = vn_rdwr(UIO_WRITE, vp, (caddr_t)buf, len, offset, - segflg, 0, rlimit, credp, &resid); + error = vn_rdwr(UIO_WRITE, vp, (caddr_t)buf, len, + (offset_t)offset, segflg, 0, rlimit, credp, &resid); if (error != 0) break; diff --git a/usr/src/uts/common/os/exec.c b/usr/src/uts/common/os/exec.c index 37ca15be86..d663f27ca0 100644 --- a/usr/src/uts/common/os/exec.c +++ b/usr/src/uts/common/os/exec.c @@ -26,7 +26,7 @@ /* Copyright (c) 1988 AT&T */ /* All Rights Reserved */ /* - * Copyright 2017 Joyent, Inc. + * Copyright 2019 Joyent, Inc. * Copyright 2022 Oxide Computer Company */ @@ -146,7 +146,7 @@ exec_common(const char *fname, const char **argp, const char **envp, proc_t *p = ttoproc(curthread); klwp_t *lwp = ttolwp(curthread); struct user *up = PTOU(p); - long execsz; /* temporary count of exec size */ + size_t execsz; /* temporary count of exec size */ int i; int error; char exec_file[MAXCOMLEN+1]; @@ -569,7 +569,7 @@ gexec( struct uarg *args, struct intpdata *idatap, int level, - long *execsz, + size_t *execsz, caddr_t exec_file, struct cred *cred, int brand_action) @@ -1451,7 +1451,7 @@ noexec( struct uarg *args, struct intpdata *idatap, int level, - long *execsz, + size_t *execsz, int setid, caddr_t exec_file, struct cred *cred) diff --git a/usr/src/uts/common/sys/brand.h b/usr/src/uts/common/sys/brand.h index badc3faff8..a2feda573d 100644 --- a/usr/src/uts/common/sys/brand.h +++ b/usr/src/uts/common/sys/brand.h @@ -21,6 +21,7 @@ /* * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2019 Joyent, Inc. */ #ifndef _SYS_BRAND_H @@ -118,10 +119,9 @@ struct brand_ops { void (*b_forklwp)(klwp_t *, klwp_t *); void (*b_freelwp)(klwp_t *); void (*b_lwpexit)(klwp_t *); - int (*b_elfexec)(struct vnode *vp, struct execa *uap, - struct uarg *args, struct intpdata *idata, int level, - long *execsz, int setid, caddr_t exec_file, - struct cred *cred, int brand_action); + int (*b_elfexec)(struct vnode *, struct execa *, struct uarg *, + struct intpdata *, int, size_t *, int, caddr_t, struct cred *, + int); void (*b_sigset_native_to_brand)(sigset_t *); void (*b_sigset_brand_to_native)(sigset_t *); int b_nsig; @@ -132,7 +132,7 @@ struct brand_ops { */ typedef struct brand { int b_version; - char *b_name; + char *b_name; struct brand_ops *b_ops; struct brand_mach_ops *b_machops; } brand_t; @@ -178,7 +178,7 @@ extern int brand_solaris_cmd(int, uintptr_t, uintptr_t, uintptr_t, extern void brand_solaris_copy_procdata(proc_t *, proc_t *, struct brand *); extern int brand_solaris_elfexec(vnode_t *, execa_t *, uarg_t *, - intpdata_t *, int, long *, int, caddr_t, cred_t *, int, + intpdata_t *, int, size_t *, int, caddr_t, cred_t *, int, struct brand *, char *, char *, char *, char *, char *); extern void brand_solaris_exec(struct brand *); extern int brand_solaris_fini(char **, struct modlinkage *, diff --git a/usr/src/uts/common/sys/exec.h b/usr/src/uts/common/sys/exec.h index 7849e75370..23eb5b6bf7 100644 --- a/usr/src/uts/common/sys/exec.h +++ b/usr/src/uts/common/sys/exec.h @@ -27,6 +27,10 @@ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ +/* + * Copyright 2019 Joyent, Inc. + */ + #ifndef _SYS_EXEC_H #define _SYS_EXEC_H @@ -80,7 +84,7 @@ typedef struct uarg { ssize_t arglen; char *fname; char *pathname; - ssize_t auxsize; + size_t auxsize; caddr_t stackend; size_t stk_align; size_t stk_size; @@ -176,7 +180,7 @@ struct execsw { int exec_maglen; int (*exec_func)(struct vnode *vp, struct execa *uap, struct uarg *args, struct intpdata *idata, int level, - long *execsz, int setid, caddr_t exec_file, + size_t *execsz, int setid, caddr_t exec_file, struct cred *cred, int brand_action); int (*exec_core)(struct vnode *vp, struct proc *p, struct cred *cred, rlim64_t rlimit, int sig, @@ -204,7 +208,7 @@ extern int exece(const char *fname, const char **argp, const char **envp); extern int exec_common(const char *fname, const char **argp, const char **envp, int brand_action); extern int gexec(vnode_t **vp, struct execa *uap, struct uarg *args, - struct intpdata *idata, int level, long *execsz, caddr_t exec_file, + struct intpdata *idata, int level, size_t *execsz, caddr_t exec_file, struct cred *cred, int brand_action); extern struct execsw *allocate_execsw(char *name, char *magic, size_t magic_size); @@ -230,26 +234,30 @@ extern void exec_set_sp(size_t); * when compiling the 32-bit compatability elf code in the elfexec module. */ extern int elfexec(vnode_t *, execa_t *, uarg_t *, intpdata_t *, int, - long *, int, caddr_t, cred_t *, int); + size_t *, int, caddr_t, cred_t *, int); extern int mapexec_brand(vnode_t *, uarg_t *, Ehdr *, Addr *, intptr_t *, caddr_t, int *, caddr_t *, caddr_t *, size_t *, uintptr_t *); +extern int elfreadhdr(vnode_t *, cred_t *, Ehdr *, uint_t *, caddr_t *, + size_t *); #endif /* !_ELF32_COMPAT */ #if defined(_LP64) extern int elf32exec(vnode_t *, execa_t *, uarg_t *, intpdata_t *, int, - long *, int, caddr_t, cred_t *, int); + size_t *, int, caddr_t, cred_t *, int); extern int mapexec32_brand(vnode_t *, uarg_t *, Elf32_Ehdr *, Elf32_Addr *, intptr_t *, caddr_t, int *, caddr_t *, caddr_t *, size_t *, uintptr_t *); +extern int elf32readhdr(vnode_t *, cred_t *, Elf32_Ehdr *, uint_t *, caddr_t *, + size_t *); #endif /* _LP64 */ /* * Utility functions for exec module core routines: */ -extern int core_seg(proc_t *, vnode_t *, offset_t, caddr_t, - size_t, rlim64_t, cred_t *); +extern int core_seg(proc_t *, vnode_t *, u_offset_t, caddr_t, size_t, + rlim64_t, cred_t *); -extern int core_write(vnode_t *, enum uio_seg, offset_t, - const void *, size_t, rlim64_t, cred_t *); +extern int core_write(vnode_t *, enum uio_seg, u_offset_t, const void *, + size_t, rlim64_t, cred_t *); #endif /* _KERNEL */ diff --git a/usr/src/uts/common/sys/prsystm.h b/usr/src/uts/common/sys/prsystm.h index 4b71191818..ec95a0409d 100644 --- a/usr/src/uts/common/sys/prsystm.h +++ b/usr/src/uts/common/sys/prsystm.h @@ -28,7 +28,7 @@ /* All Rights Reserved */ /* - * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright 2019 Joyent, Inc. * Copyright 2020 OmniOS Community Edition (OmniOSce) Association. */ @@ -83,7 +83,7 @@ extern void prgetcred(proc_t *, struct prcred *); extern void prgetpriv(proc_t *, struct prpriv *); extern size_t prgetprivsize(void); extern void prgetsecflags(proc_t *, struct prsecflags *); -extern int prnsegs(struct as *, int); +extern uint_t prnsegs(struct as *, int); extern u_offset_t prgetfdinfosize(proc_t *, vnode_t *, cred_t *); extern int prgetfdinfo(proc_t *, vnode_t *, struct prfdinfo *, cred_t *, cred_t *, list_t *); |