summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/uts/common/brand/sn1/sn1_brand.c7
-rw-r--r--usr/src/uts/common/brand/solaris10/s10_brand.c8
-rw-r--r--usr/src/uts/common/exec/elf/elf.c1286
-rw-r--r--usr/src/uts/common/exec/elf/elf_impl.h17
-rw-r--r--usr/src/uts/common/exec/intp/intp.c9
-rw-r--r--usr/src/uts/common/exec/java/java.c3
-rw-r--r--usr/src/uts/common/exec/shbin/shbin.c5
-rw-r--r--usr/src/uts/common/fs/proc/prioctl.c10
-rw-r--r--usr/src/uts/common/fs/proc/prsubr.c21
-rw-r--r--usr/src/uts/common/os/brand.c3
-rw-r--r--usr/src/uts/common/os/core.c22
-rw-r--r--usr/src/uts/common/os/exec.c8
-rw-r--r--usr/src/uts/common/sys/brand.h12
-rw-r--r--usr/src/uts/common/sys/exec.h26
-rw-r--r--usr/src/uts/common/sys/prsystm.h4
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 *);