diff options
author | Jerry Jelinek <jerry.jelinek@joyent.com> | 2014-12-09 18:20:38 +0000 |
---|---|---|
committer | Jerry Jelinek <jerry.jelinek@joyent.com> | 2014-12-09 18:20:38 +0000 |
commit | f75517a60572a02b8b794334dc59a4cf7bfae1d7 (patch) | |
tree | a485a1f87355e2ab37a623a73c02a76e9f2e4cdd | |
parent | e56cfc34cca8562a7d4f2c03dd32adcc6abaacf3 (diff) | |
download | illumos-joyent-f75517a60572a02b8b794334dc59a4cf7bfae1d7.tar.gz |
OS-3615 elfexec likes to play fast and loose
-rw-r--r-- | usr/src/uts/common/exec/elf/elf.c | 29 | ||||
-rw-r--r-- | usr/src/uts/common/os/exec.c | 54 |
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 = |