summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJerry Jelinek <jerry.jelinek@joyent.com>2014-12-09 18:20:38 +0000
committerJerry Jelinek <jerry.jelinek@joyent.com>2014-12-09 18:20:38 +0000
commitf75517a60572a02b8b794334dc59a4cf7bfae1d7 (patch)
treea485a1f87355e2ab37a623a73c02a76e9f2e4cdd
parente56cfc34cca8562a7d4f2c03dd32adcc6abaacf3 (diff)
downloadillumos-joyent-f75517a60572a02b8b794334dc59a4cf7bfae1d7.tar.gz
OS-3615 elfexec likes to play fast and loose
-rw-r--r--usr/src/uts/common/exec/elf/elf.c29
-rw-r--r--usr/src/uts/common/os/exec.c54
2 files changed, 67 insertions, 16 deletions
diff --git a/usr/src/uts/common/exec/elf/elf.c b/usr/src/uts/common/exec/elf/elf.c
index 1f16b377b1..f1ada05872 100644
--- a/usr/src/uts/common/exec/elf/elf.c
+++ b/usr/src/uts/common/exec/elf/elf.c
@@ -482,12 +482,12 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
* AT_BASE
* AT_FLAGS
* AT_PAGESZ
- * AT_RANDOM
- * AT_SUN_LDSECURE
+ * AT_RANDOM (added in stk_copyout)
+ * AT_SUN_AUXFLAGS
* AT_SUN_HWCAP
* AT_SUN_HWCAP2
- * AT_SUN_PLATFORM
- * AT_SUN_EXECNAME
+ * AT_SUN_PLATFORM (added in stk_copyout)
+ * AT_SUN_EXECNAME (added in stk_copyout)
* AT_NULL
*
* total == 10
@@ -575,6 +575,8 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
aux = bigwad->elfargs;
/*
* Move args to the user's stack.
+ * This can fill in the AT_SUN_PLATFORM, AT_SUN_EXECNAME and AT_RANDOM
+ * aux entries.
*/
if ((error = exec_args(uap, args, idatap, (void **)&aux)) != 0) {
if (error == -1) {
@@ -796,8 +798,8 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
if (hasauxv) {
int auxf = AF_SUN_HWCAPVERIFY;
/*
- * Note: AT_SUN_PLATFORM and AT_RANDOM were filled in via
- * exec_args()
+ * Note: AT_SUN_PLATFORM, AT_SUN_EXECNAME and AT_RANDOM were
+ * filled in via exec_args()
*/
ADDAUX(aux, AT_BASE, voffset)
ADDAUX(aux, AT_FLAGS, at_flags)
@@ -872,7 +874,20 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
ADDAUX(aux, AT_NULL, 0)
postfixsize = (char *)aux - (char *)bigwad->elfargs;
- ASSERT(postfixsize == args->auxsize);
+
+ /*
+ * We make assumptions above when we determine how many aux
+ * vector entries we will be adding. However, if we have an
+ * invalid elf file, it is possible that mapelfexec might
+ * behave differently (but not return an error), in which case
+ * the number of aux entries we actually add will be different.
+ * We detect that now and error out.
+ */
+ if (postfixsize != args->auxsize) {
+ DTRACE_PROBE2(elfexec_badaux, int, postfixsize,
+ int, args->auxsize);
+ goto bad;
+ }
ASSERT(postfixsize <= __KERN_NAUXV_IMPL * sizeof (aux_entry_t));
}
diff --git a/usr/src/uts/common/os/exec.c b/usr/src/uts/common/os/exec.c
index e896bbd1e5..d044b2a08d 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 (c) 2012, Joyent, Inc. All rights reserved.
+ * Copyright 2014, Joyent, Inc. All rights reserved.
*/
#include <sys/types.h>
@@ -1270,6 +1270,33 @@ execmap(struct vnode *vp, caddr_t addr, size_t len, size_t zfodlen,
/*
* Before we go to zero the remaining space on the last
* page, make sure we have write permission.
+ *
+ * Normal illumos binaries don't even hit the case
+ * where we have to change permission on the last page
+ * since their protection is typically either
+ * PROT_USER | PROT_WRITE | PROT_READ
+ * or
+ * PROT_ZFOD (same as PROT_ALL).
+ *
+ * We need to be careful how we zero-fill the last page
+ * if the segment protection does not include
+ * PROT_WRITE. We don't want to use as_setprot() to
+ * temporarily change the last page's prot to enable
+ * PROT_WRITE, since that can cause the VM segment code
+ * to call seg*_vpage(), which will allocate a page
+ * struct for each page in the segment. If we have a
+ * very large segment, this will either use a large
+ * amount of kernel memory, or possibly never finish
+ * because page_resv() will never obtain enough free
+ * pages.
+ *
+ * Instead, we temporarily change the protection on the
+ * entire segment so that we can zero-fill the last
+ * page, then change the protection back.
+ *
+ * Because we are working with the entire segement, the
+ * VM code does not need to allocate per-page structs
+ * to keep track of permissions.
*/
AS_LOCK_ENTER(as, &as->a_lock, RW_READER);
@@ -1280,23 +1307,32 @@ execmap(struct vnode *vp, caddr_t addr, size_t len, size_t zfodlen,
AS_LOCK_EXIT(as, &as->a_lock);
if (seg != NULL && (zprot & PROT_WRITE) == 0) {
- (void) as_setprot(as, (caddr_t)end,
- zfoddiff - 1, zprot | PROT_WRITE);
+ AS_LOCK_ENTER(as, &as->a_lock, RW_WRITER);
+ (void) SEGOP_SETPROT(seg, seg->s_base,
+ seg->s_size, zprot | PROT_WRITE);
+ AS_LOCK_EXIT(as, &as->a_lock);
}
if (on_fault(&ljb)) {
no_fault();
- if (seg != NULL && (zprot & PROT_WRITE) == 0)
- (void) as_setprot(as, (caddr_t)end,
- zfoddiff - 1, zprot);
+ if (seg != NULL && (zprot & PROT_WRITE) == 0) {
+ AS_LOCK_ENTER(as, &as->a_lock,
+ RW_WRITER);
+ (void) SEGOP_SETPROT(seg, seg->s_base,
+ seg->s_size, zprot);
+ AS_LOCK_EXIT(as, &as->a_lock);
+ }
error = EFAULT;
goto bad;
}
uzero((void *)end, zfoddiff);
no_fault();
- if (seg != NULL && (zprot & PROT_WRITE) == 0)
- (void) as_setprot(as, (caddr_t)end,
- zfoddiff - 1, zprot);
+ if (seg != NULL && (zprot & PROT_WRITE) == 0) {
+ AS_LOCK_ENTER(as, &as->a_lock, RW_WRITER);
+ (void) SEGOP_SETPROT(seg, seg->s_base,
+ seg->s_size, zprot);
+ AS_LOCK_EXIT(as, &as->a_lock);
+ }
}
if (zfodlen > zfoddiff) {
struct segvn_crargs crargs =