2015-08-05 18:58:01 +03:00
|
|
|
/* $NetBSD: exec_elf.c,v 1.75 2015/08/05 15:58:01 maxv Exp $ */
|
2008-11-19 21:35:57 +03:00
|
|
|
|
|
|
|
/*-
|
|
|
|
* Copyright (c) 1994, 2000, 2005 The NetBSD Foundation, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
|
|
* by Christos Zoulas.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 1996 Christopher G. Demetriou
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
|
|
* derived from this software without specific prior written permission
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
2015-08-05 18:58:01 +03:00
|
|
|
__KERNEL_RCSID(1, "$NetBSD: exec_elf.c,v 1.75 2015/08/05 15:58:01 maxv Exp $");
|
2008-11-19 21:35:57 +03:00
|
|
|
|
|
|
|
#ifdef _KERNEL_OPT
|
|
|
|
#include "opt_pax.h"
|
|
|
|
#endif /* _KERNEL_OPT */
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/proc.h>
|
|
|
|
#include <sys/kmem.h>
|
|
|
|
#include <sys/namei.h>
|
|
|
|
#include <sys/vnode.h>
|
|
|
|
#include <sys/exec.h>
|
|
|
|
#include <sys/exec_elf.h>
|
|
|
|
#include <sys/syscall.h>
|
|
|
|
#include <sys/signalvar.h>
|
|
|
|
#include <sys/mount.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/kauth.h>
|
|
|
|
#include <sys/bitops.h>
|
First step of random number subsystem rework described in
<20111022023242.BA26F14A158@mail.netbsd.org>. This change includes
the following:
An initial cleanup and minor reorganization of the entropy pool
code in sys/dev/rnd.c and sys/dev/rndpool.c. Several bugs are
fixed. Some effort is made to accumulate entropy more quickly at
boot time.
A generic interface, "rndsink", is added, for stream generators to
request that they be re-keyed with good quality entropy from the pool
as soon as it is available.
The arc4random()/arc4randbytes() implementation in libkern is
adjusted to use the rndsink interface for rekeying, which helps
address the problem of low-quality keys at boot time.
An implementation of the FIPS 140-2 statistical tests for random
number generator quality is provided (libkern/rngtest.c). This
is based on Greg Rose's implementation from Qualcomm.
A new random stream generator, nist_ctr_drbg, is provided. It is
based on an implementation of the NIST SP800-90 CTR_DRBG by
Henric Jungheim. This generator users AES in a modified counter
mode to generate a backtracking-resistant random stream.
An abstraction layer, "cprng", is provided for in-kernel consumers
of randomness. The arc4random/arc4randbytes API is deprecated for
in-kernel use. It is replaced by "cprng_strong". The current
cprng_fast implementation wraps the existing arc4random
implementation. The current cprng_strong implementation wraps the
new CTR_DRBG implementation. Both interfaces are rekeyed from
the entropy pool automatically at intervals justifiable from best
current cryptographic practice.
In some quick tests, cprng_fast() is about the same speed as
the old arc4randbytes(), and cprng_strong() is about 20% faster
than rnd_extract_data(). Performance is expected to improve.
The AES code in src/crypto/rijndael is no longer an optional
kernel component, as it is required by cprng_strong, which is
not an optional kernel component.
The entropy pool output is subjected to the rngtest tests at
startup time; if it fails, the system will reboot. There is
approximately a 3/10000 chance of a false positive from these
tests. Entropy pool _input_ from hardware random numbers is
subjected to the rngtest tests at attach time, as well as the
FIPS continuous-output test, to detect bad or stuck hardware
RNGs; if any are detected, they are detached, but the system
continues to run.
A problem with rndctl(8) is fixed -- datastructures with
pointers in arrays are no longer passed to userspace (this
was not a security problem, but rather a major issue for
compat32). A new kernel will require a new rndctl.
The sysctl kern.arandom() and kern.urandom() nodes are hooked
up to the new generators, but the /dev/*random pseudodevices
are not, yet.
Manual pages for the new kernel interfaces are forthcoming.
2011-11-20 02:51:18 +04:00
|
|
|
#include <sys/cprng.h>
|
2008-11-19 21:35:57 +03:00
|
|
|
|
|
|
|
#include <sys/cpu.h>
|
|
|
|
#include <machine/reg.h>
|
|
|
|
|
|
|
|
#include <compat/common/compat_util.h>
|
|
|
|
|
|
|
|
#include <sys/pax.h>
|
2012-02-12 03:16:15 +04:00
|
|
|
#include <uvm/uvm_param.h>
|
2008-11-19 21:35:57 +03:00
|
|
|
|
|
|
|
extern struct emul emul_netbsd;
|
|
|
|
|
|
|
|
#define elf_check_header ELFNAME(check_header)
|
|
|
|
#define elf_copyargs ELFNAME(copyargs)
|
2014-03-22 11:27:21 +04:00
|
|
|
#define elf_load_interp ELFNAME(load_interp)
|
2008-11-19 21:35:57 +03:00
|
|
|
#define elf_load_psection ELFNAME(load_psection)
|
|
|
|
#define exec_elf_makecmds ELFNAME2(exec,makecmds)
|
|
|
|
#define netbsd_elf_signature ELFNAME2(netbsd,signature)
|
|
|
|
#define netbsd_elf_probe ELFNAME2(netbsd,probe)
|
|
|
|
#define coredump ELFNAMEEND(coredump)
|
2012-02-04 00:11:53 +04:00
|
|
|
#define elf_free_emul_arg ELFNAME(free_emul_arg)
|
2008-11-19 21:35:57 +03:00
|
|
|
|
2014-02-16 21:46:36 +04:00
|
|
|
static int
|
2014-03-22 11:27:21 +04:00
|
|
|
elf_load_interp(struct lwp *, struct exec_package *, char *,
|
2014-02-16 21:46:36 +04:00
|
|
|
struct exec_vmcmd_set *, u_long *, Elf_Addr *);
|
|
|
|
static void
|
|
|
|
elf_load_psection(struct exec_vmcmd_set *, struct vnode *, const Elf_Phdr *,
|
2014-03-16 11:57:25 +04:00
|
|
|
Elf_Addr *, u_long *, int);
|
2008-11-19 21:35:57 +03:00
|
|
|
|
|
|
|
int netbsd_elf_signature(struct lwp *, struct exec_package *, Elf_Ehdr *);
|
|
|
|
int netbsd_elf_probe(struct lwp *, struct exec_package *, void *, char *,
|
|
|
|
vaddr_t *);
|
|
|
|
|
2012-02-04 00:11:53 +04:00
|
|
|
static void elf_free_emul_arg(void *);
|
|
|
|
|
2008-11-19 21:35:57 +03:00
|
|
|
/* round up and down to page boundaries. */
|
|
|
|
#define ELF_ROUND(a, b) (((a) + (b) - 1) & ~((b) - 1))
|
|
|
|
#define ELF_TRUNC(a, b) ((a) & ~((b) - 1))
|
|
|
|
|
|
|
|
static void
|
2010-03-20 04:45:30 +03:00
|
|
|
elf_placedynexec(struct lwp *l, struct exec_package *epp, Elf_Ehdr *eh,
|
2008-11-19 21:35:57 +03:00
|
|
|
Elf_Phdr *ph)
|
|
|
|
{
|
2010-03-23 01:10:10 +03:00
|
|
|
Elf_Addr align, offset;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (align = i = 0; i < eh->e_phnum; i++)
|
|
|
|
if (ph[i].p_type == PT_LOAD && ph[i].p_align > align)
|
|
|
|
align = ph[i].p_align;
|
2008-11-19 21:35:57 +03:00
|
|
|
|
2010-03-20 04:45:30 +03:00
|
|
|
#ifdef PAX_ASLR
|
2010-03-20 01:08:13 +03:00
|
|
|
if (pax_aslr_active(l)) {
|
|
|
|
size_t pax_align, l2, delta;
|
|
|
|
uint32_t r;
|
2008-11-19 21:35:57 +03:00
|
|
|
|
2010-03-23 01:10:10 +03:00
|
|
|
pax_align = align;
|
2008-11-19 21:35:57 +03:00
|
|
|
|
First step of random number subsystem rework described in
<20111022023242.BA26F14A158@mail.netbsd.org>. This change includes
the following:
An initial cleanup and minor reorganization of the entropy pool
code in sys/dev/rnd.c and sys/dev/rndpool.c. Several bugs are
fixed. Some effort is made to accumulate entropy more quickly at
boot time.
A generic interface, "rndsink", is added, for stream generators to
request that they be re-keyed with good quality entropy from the pool
as soon as it is available.
The arc4random()/arc4randbytes() implementation in libkern is
adjusted to use the rndsink interface for rekeying, which helps
address the problem of low-quality keys at boot time.
An implementation of the FIPS 140-2 statistical tests for random
number generator quality is provided (libkern/rngtest.c). This
is based on Greg Rose's implementation from Qualcomm.
A new random stream generator, nist_ctr_drbg, is provided. It is
based on an implementation of the NIST SP800-90 CTR_DRBG by
Henric Jungheim. This generator users AES in a modified counter
mode to generate a backtracking-resistant random stream.
An abstraction layer, "cprng", is provided for in-kernel consumers
of randomness. The arc4random/arc4randbytes API is deprecated for
in-kernel use. It is replaced by "cprng_strong". The current
cprng_fast implementation wraps the existing arc4random
implementation. The current cprng_strong implementation wraps the
new CTR_DRBG implementation. Both interfaces are rekeyed from
the entropy pool automatically at intervals justifiable from best
current cryptographic practice.
In some quick tests, cprng_fast() is about the same speed as
the old arc4randbytes(), and cprng_strong() is about 20% faster
than rnd_extract_data(). Performance is expected to improve.
The AES code in src/crypto/rijndael is no longer an optional
kernel component, as it is required by cprng_strong, which is
not an optional kernel component.
The entropy pool output is subjected to the rngtest tests at
startup time; if it fails, the system will reboot. There is
approximately a 3/10000 chance of a false positive from these
tests. Entropy pool _input_ from hardware random numbers is
subjected to the rngtest tests at attach time, as well as the
FIPS continuous-output test, to detect bad or stuck hardware
RNGs; if any are detected, they are detached, but the system
continues to run.
A problem with rndctl(8) is fixed -- datastructures with
pointers in arrays are no longer passed to userspace (this
was not a security problem, but rather a major issue for
compat32). A new kernel will require a new rndctl.
The sysctl kern.arandom() and kern.urandom() nodes are hooked
up to the new generators, but the /dev/*random pseudodevices
are not, yet.
Manual pages for the new kernel interfaces are forthcoming.
2011-11-20 02:51:18 +04:00
|
|
|
r = cprng_fast32();
|
2008-11-19 21:35:57 +03:00
|
|
|
|
2010-03-20 01:08:13 +03:00
|
|
|
if (pax_align == 0)
|
|
|
|
pax_align = PGSHIFT;
|
|
|
|
l2 = ilog2(pax_align);
|
|
|
|
delta = PAX_ASLR_DELTA(r, l2, PAX_ASLR_DELTA_EXEC_LEN);
|
2010-03-20 04:45:30 +03:00
|
|
|
offset = ELF_TRUNC(delta, pax_align) + PAGE_SIZE;
|
2010-03-20 04:47:12 +03:00
|
|
|
#ifdef PAX_ASLR_DEBUG
|
2010-03-20 04:52:16 +03:00
|
|
|
uprintf("r=0x%x l2=0x%zx PGSHIFT=0x%x Delta=0x%zx\n", r, l2,
|
|
|
|
PGSHIFT, delta);
|
2010-04-02 18:11:18 +04:00
|
|
|
uprintf("pax offset=0x%llx entry=0x%llx\n",
|
|
|
|
(unsigned long long)offset,
|
|
|
|
(unsigned long long)eh->e_entry);
|
2010-03-20 04:47:12 +03:00
|
|
|
#endif /* PAX_ASLR_DEBUG */
|
2010-03-20 01:08:13 +03:00
|
|
|
} else
|
2010-03-20 04:45:30 +03:00
|
|
|
#endif /* PAX_ASLR */
|
2010-03-23 01:10:10 +03:00
|
|
|
offset = MAX(align, PAGE_SIZE);
|
2008-11-19 21:35:57 +03:00
|
|
|
|
2011-08-27 21:53:21 +04:00
|
|
|
offset += epp->ep_vm_minaddr;
|
|
|
|
|
2008-11-19 21:35:57 +03:00
|
|
|
for (i = 0; i < eh->e_phnum; i++)
|
2010-03-20 04:52:16 +03:00
|
|
|
ph[i].p_vaddr += offset;
|
2014-03-06 13:30:37 +04:00
|
|
|
epp->ep_entryoffset = offset;
|
2010-03-20 04:45:30 +03:00
|
|
|
eh->e_entry += offset;
|
2008-11-19 21:35:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copy arguments onto the stack in the normal way, but add some
|
|
|
|
* extra information in case of dynamic binding.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
elf_copyargs(struct lwp *l, struct exec_package *pack,
|
|
|
|
struct ps_strings *arginfo, char **stackp, void *argp)
|
|
|
|
{
|
|
|
|
size_t len, vlen;
|
|
|
|
AuxInfo ai[ELF_AUX_ENTRIES], *a, *execname;
|
|
|
|
struct elf_args *ap;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if ((error = copyargs(l, pack, arginfo, stackp, argp)) != 0)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
a = ai;
|
|
|
|
execname = NULL;
|
|
|
|
|
2015-03-20 23:36:27 +03:00
|
|
|
memset(ai, 0, sizeof(ai));
|
|
|
|
|
2008-11-19 21:35:57 +03:00
|
|
|
/*
|
|
|
|
* Push extra arguments on the stack needed by dynamically
|
|
|
|
* linked binaries
|
|
|
|
*/
|
|
|
|
if ((ap = (struct elf_args *)pack->ep_emul_arg)) {
|
|
|
|
struct vattr *vap = pack->ep_vap;
|
|
|
|
|
|
|
|
a->a_type = AT_PHDR;
|
|
|
|
a->a_v = ap->arg_phaddr;
|
|
|
|
a++;
|
|
|
|
|
|
|
|
a->a_type = AT_PHENT;
|
|
|
|
a->a_v = ap->arg_phentsize;
|
|
|
|
a++;
|
|
|
|
|
|
|
|
a->a_type = AT_PHNUM;
|
|
|
|
a->a_v = ap->arg_phnum;
|
|
|
|
a++;
|
|
|
|
|
|
|
|
a->a_type = AT_PAGESZ;
|
|
|
|
a->a_v = PAGE_SIZE;
|
|
|
|
a++;
|
|
|
|
|
|
|
|
a->a_type = AT_BASE;
|
|
|
|
a->a_v = ap->arg_interp;
|
|
|
|
a++;
|
|
|
|
|
|
|
|
a->a_type = AT_FLAGS;
|
|
|
|
a->a_v = 0;
|
|
|
|
a++;
|
|
|
|
|
|
|
|
a->a_type = AT_ENTRY;
|
|
|
|
a->a_v = ap->arg_entry;
|
|
|
|
a++;
|
|
|
|
|
|
|
|
a->a_type = AT_EUID;
|
|
|
|
if (vap->va_mode & S_ISUID)
|
|
|
|
a->a_v = vap->va_uid;
|
|
|
|
else
|
|
|
|
a->a_v = kauth_cred_geteuid(l->l_cred);
|
|
|
|
a++;
|
|
|
|
|
|
|
|
a->a_type = AT_RUID;
|
|
|
|
a->a_v = kauth_cred_getuid(l->l_cred);
|
|
|
|
a++;
|
|
|
|
|
|
|
|
a->a_type = AT_EGID;
|
|
|
|
if (vap->va_mode & S_ISGID)
|
|
|
|
a->a_v = vap->va_gid;
|
|
|
|
else
|
|
|
|
a->a_v = kauth_cred_getegid(l->l_cred);
|
|
|
|
a++;
|
|
|
|
|
|
|
|
a->a_type = AT_RGID;
|
|
|
|
a->a_v = kauth_cred_getgid(l->l_cred);
|
|
|
|
a++;
|
|
|
|
|
2012-02-04 22:12:02 +04:00
|
|
|
a->a_type = AT_STACKBASE;
|
|
|
|
a->a_v = l->l_proc->p_stackbase;
|
|
|
|
a++;
|
|
|
|
|
2008-11-19 21:35:57 +03:00
|
|
|
if (pack->ep_path) {
|
|
|
|
execname = a;
|
|
|
|
a->a_type = AT_SUN_EXECNAME;
|
|
|
|
a++;
|
|
|
|
}
|
|
|
|
|
2012-02-04 00:11:53 +04:00
|
|
|
exec_free_emul_arg(pack);
|
2008-11-19 21:35:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
a->a_type = AT_NULL;
|
|
|
|
a->a_v = 0;
|
|
|
|
a++;
|
|
|
|
|
2012-02-04 22:12:02 +04:00
|
|
|
vlen = (a - ai) * sizeof(ai[0]);
|
|
|
|
|
|
|
|
KASSERT(vlen <= sizeof(ai));
|
2008-11-19 21:35:57 +03:00
|
|
|
|
|
|
|
if (execname) {
|
|
|
|
char *path = pack->ep_path;
|
|
|
|
execname->a_v = (uintptr_t)(*stackp + vlen);
|
|
|
|
len = strlen(path) + 1;
|
|
|
|
if ((error = copyout(path, (*stackp + vlen), len)) != 0)
|
|
|
|
return error;
|
|
|
|
len = ALIGN(len);
|
|
|
|
} else
|
|
|
|
len = 0;
|
|
|
|
|
|
|
|
if ((error = copyout(ai, *stackp, vlen)) != 0)
|
|
|
|
return error;
|
|
|
|
*stackp += vlen + len;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* elf_check_header():
|
|
|
|
*
|
2013-08-26 16:24:10 +04:00
|
|
|
* Check header for validity; return 0 if ok, ENOEXEC if error
|
2008-11-19 21:35:57 +03:00
|
|
|
*/
|
|
|
|
int
|
2014-02-15 20:17:01 +04:00
|
|
|
elf_check_header(Elf_Ehdr *eh)
|
2008-11-19 21:35:57 +03:00
|
|
|
{
|
|
|
|
|
|
|
|
if (memcmp(eh->e_ident, ELFMAG, SELFMAG) != 0 ||
|
|
|
|
eh->e_ident[EI_CLASS] != ELFCLASS)
|
|
|
|
return ENOEXEC;
|
|
|
|
|
|
|
|
switch (eh->e_machine) {
|
|
|
|
|
|
|
|
ELFDEFNNAME(MACHDEP_ID_CASES)
|
|
|
|
|
|
|
|
default:
|
|
|
|
return ENOEXEC;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ELF_EHDR_FLAGS_OK(eh) == 0)
|
|
|
|
return ENOEXEC;
|
|
|
|
|
2014-07-08 21:16:25 +04:00
|
|
|
if (eh->e_shnum > ELF_MAXSHNUM || eh->e_phnum > ELF_MAXPHNUM)
|
2008-11-19 21:35:57 +03:00
|
|
|
return ENOEXEC;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* elf_load_psection():
|
|
|
|
*
|
|
|
|
* Load a psection at the appropriate address
|
|
|
|
*/
|
2014-02-16 21:46:36 +04:00
|
|
|
static void
|
2008-11-19 21:35:57 +03:00
|
|
|
elf_load_psection(struct exec_vmcmd_set *vcset, struct vnode *vp,
|
2014-03-16 11:57:25 +04:00
|
|
|
const Elf_Phdr *ph, Elf_Addr *addr, u_long *size, int flags)
|
2008-11-19 21:35:57 +03:00
|
|
|
{
|
|
|
|
u_long msize, psize, rm, rf;
|
|
|
|
long diff, offset;
|
2014-03-16 11:57:25 +04:00
|
|
|
int vmprot = 0;
|
2008-11-19 21:35:57 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the user specified an address, then we load there.
|
|
|
|
*/
|
|
|
|
if (*addr == ELFDEFNNAME(NO_ADDR))
|
|
|
|
*addr = ph->p_vaddr;
|
|
|
|
|
|
|
|
if (ph->p_align > 1) {
|
|
|
|
/*
|
|
|
|
* Make sure we are virtually aligned as we are supposed to be.
|
|
|
|
*/
|
|
|
|
diff = ph->p_vaddr - ELF_TRUNC(ph->p_vaddr, ph->p_align);
|
|
|
|
KASSERT(*addr - diff == ELF_TRUNC(*addr, ph->p_align));
|
|
|
|
/*
|
|
|
|
* But make sure to not map any pages before the start of the
|
|
|
|
* psection by limiting the difference to within a page.
|
|
|
|
*/
|
|
|
|
diff &= PAGE_MASK;
|
|
|
|
} else
|
|
|
|
diff = 0;
|
|
|
|
|
2014-03-16 11:57:25 +04:00
|
|
|
vmprot |= (ph->p_flags & PF_R) ? VM_PROT_READ : 0;
|
|
|
|
vmprot |= (ph->p_flags & PF_W) ? VM_PROT_WRITE : 0;
|
|
|
|
vmprot |= (ph->p_flags & PF_X) ? VM_PROT_EXECUTE : 0;
|
2008-11-19 21:35:57 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Adjust everything so it all starts on a page boundary.
|
|
|
|
*/
|
|
|
|
*addr -= diff;
|
|
|
|
offset = ph->p_offset - diff;
|
|
|
|
*size = ph->p_filesz + diff;
|
|
|
|
msize = ph->p_memsz + diff;
|
|
|
|
|
|
|
|
if (ph->p_align >= PAGE_SIZE) {
|
|
|
|
if ((ph->p_flags & PF_W) != 0) {
|
|
|
|
/*
|
|
|
|
* Because the pagedvn pager can't handle zero fill
|
|
|
|
* of the last data page if it's not page aligned we
|
|
|
|
* map the last page readvn.
|
|
|
|
*/
|
|
|
|
psize = trunc_page(*size);
|
|
|
|
} else {
|
|
|
|
psize = round_page(*size);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
psize = *size;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (psize > 0) {
|
|
|
|
NEW_VMCMD2(vcset, ph->p_align < PAGE_SIZE ?
|
|
|
|
vmcmd_map_readvn : vmcmd_map_pagedvn, psize, *addr, vp,
|
2014-03-16 11:57:25 +04:00
|
|
|
offset, vmprot, flags);
|
2008-11-19 21:35:57 +03:00
|
|
|
flags &= VMCMD_RELATIVE;
|
|
|
|
}
|
|
|
|
if (psize < *size) {
|
|
|
|
NEW_VMCMD2(vcset, vmcmd_map_readvn, *size - psize,
|
2014-03-16 11:57:25 +04:00
|
|
|
*addr + psize, vp, offset + psize, vmprot, flags);
|
2008-11-19 21:35:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if we need to extend the size of the segment (does
|
|
|
|
* bss extend page the next page boundary)?
|
|
|
|
*/
|
|
|
|
rm = round_page(*addr + msize);
|
|
|
|
rf = round_page(*addr + *size);
|
|
|
|
|
|
|
|
if (rm != rf) {
|
|
|
|
NEW_VMCMD2(vcset, vmcmd_map_zero, rm - rf, rf, NULLVP,
|
2014-03-16 11:57:25 +04:00
|
|
|
0, vmprot, flags & VMCMD_RELATIVE);
|
2008-11-19 21:35:57 +03:00
|
|
|
*size = msize;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2014-03-22 11:27:21 +04:00
|
|
|
* elf_load_interp():
|
2008-11-19 21:35:57 +03:00
|
|
|
*
|
2014-03-22 11:27:21 +04:00
|
|
|
* Load an interpreter pointed to by path.
|
2008-11-19 21:35:57 +03:00
|
|
|
*/
|
2014-02-16 21:46:36 +04:00
|
|
|
static int
|
2014-03-22 11:27:21 +04:00
|
|
|
elf_load_interp(struct lwp *l, struct exec_package *epp, char *path,
|
2014-02-16 21:46:36 +04:00
|
|
|
struct exec_vmcmd_set *vcset, u_long *entryoff, Elf_Addr *last)
|
2008-11-19 21:35:57 +03:00
|
|
|
{
|
|
|
|
int error, i;
|
|
|
|
struct vnode *vp;
|
|
|
|
struct vattr attr;
|
|
|
|
Elf_Ehdr eh;
|
|
|
|
Elf_Phdr *ph = NULL;
|
|
|
|
const Elf_Phdr *base_ph;
|
|
|
|
const Elf_Phdr *last_ph;
|
|
|
|
u_long phsize;
|
|
|
|
Elf_Addr addr = *last;
|
|
|
|
struct proc *p;
|
2012-02-12 03:16:15 +04:00
|
|
|
bool use_topdown;
|
2008-11-19 21:35:57 +03:00
|
|
|
|
|
|
|
p = l->l_proc;
|
|
|
|
|
2012-04-08 15:27:44 +04:00
|
|
|
KASSERT(p->p_vmspace);
|
2013-11-14 16:07:11 +04:00
|
|
|
if (__predict_true(p->p_vmspace != proc0.p_vmspace)) {
|
2012-02-12 03:16:15 +04:00
|
|
|
use_topdown = p->p_vmspace->vm_map.flags & VM_MAP_TOPDOWN;
|
2013-11-14 16:07:11 +04:00
|
|
|
} else {
|
2014-01-25 23:44:11 +04:00
|
|
|
#ifdef __USE_TOPDOWN_VM
|
2013-11-14 16:07:11 +04:00
|
|
|
use_topdown = epp->ep_flags & EXEC_TOPDOWN_VM;
|
2012-02-12 03:16:15 +04:00
|
|
|
#else
|
|
|
|
use_topdown = false;
|
|
|
|
#endif
|
2013-11-14 16:07:11 +04:00
|
|
|
}
|
2012-02-12 03:16:15 +04:00
|
|
|
|
2008-11-19 21:35:57 +03:00
|
|
|
/*
|
|
|
|
* 1. open file
|
|
|
|
* 2. read filehdr
|
|
|
|
* 3. map text, data, and bss out of it using VM_*
|
|
|
|
*/
|
|
|
|
vp = epp->ep_interp;
|
|
|
|
if (vp == NULL) {
|
|
|
|
error = emul_find_interp(l, epp, path);
|
|
|
|
if (error != 0)
|
|
|
|
return error;
|
|
|
|
vp = epp->ep_interp;
|
|
|
|
}
|
|
|
|
/* We'll tidy this ourselves - otherwise we have locking issues */
|
|
|
|
epp->ep_interp = NULL;
|
|
|
|
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Similarly, if it's not marked as executable, or it's not a regular
|
|
|
|
* file, we don't allow it to be used.
|
|
|
|
*/
|
|
|
|
if (vp->v_type != VREG) {
|
|
|
|
error = EACCES;
|
|
|
|
goto badunlock;
|
|
|
|
}
|
|
|
|
if ((error = VOP_ACCESS(vp, VEXEC, l->l_cred)) != 0)
|
|
|
|
goto badunlock;
|
|
|
|
|
|
|
|
/* get attributes */
|
|
|
|
if ((error = VOP_GETATTR(vp, &attr, l->l_cred)) != 0)
|
|
|
|
goto badunlock;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check mount point. Though we're not trying to exec this binary,
|
|
|
|
* we will be executing code from it, so if the mount point
|
|
|
|
* disallows execution or set-id-ness, we punt or kill the set-id.
|
|
|
|
*/
|
|
|
|
if (vp->v_mount->mnt_flag & MNT_NOEXEC) {
|
|
|
|
error = EACCES;
|
|
|
|
goto badunlock;
|
|
|
|
}
|
|
|
|
if (vp->v_mount->mnt_flag & MNT_NOSUID)
|
|
|
|
epp->ep_vap->va_mode &= ~(S_ISUID | S_ISGID);
|
|
|
|
|
|
|
|
error = vn_marktext(vp);
|
|
|
|
if (error)
|
|
|
|
goto badunlock;
|
|
|
|
|
2010-06-24 16:58:48 +04:00
|
|
|
VOP_UNLOCK(vp);
|
2008-11-19 21:35:57 +03:00
|
|
|
|
|
|
|
if ((error = exec_read_from(l, vp, 0, &eh, sizeof(eh))) != 0)
|
|
|
|
goto bad;
|
|
|
|
|
2014-02-15 20:17:01 +04:00
|
|
|
if ((error = elf_check_header(&eh)) != 0)
|
2008-11-19 21:35:57 +03:00
|
|
|
goto bad;
|
2014-02-15 20:17:01 +04:00
|
|
|
if (eh.e_type != ET_DYN || eh.e_phnum == 0) {
|
2008-11-19 21:35:57 +03:00
|
|
|
error = ENOEXEC;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
phsize = eh.e_phnum * sizeof(Elf_Phdr);
|
|
|
|
ph = kmem_alloc(phsize, KM_SLEEP);
|
|
|
|
|
|
|
|
if ((error = exec_read_from(l, vp, eh.e_phoff, ph, phsize)) != 0)
|
|
|
|
goto bad;
|
|
|
|
|
|
|
|
#ifdef ELF_INTERP_NON_RELOCATABLE
|
|
|
|
/*
|
|
|
|
* Evil hack: Only MIPS should be non-relocatable, and the
|
|
|
|
* psections should have a high address (typically 0x5ffe0000).
|
|
|
|
* If it's now relocatable, it should be linked at 0 and the
|
|
|
|
* psections should have zeros in the upper part of the address.
|
|
|
|
* Otherwise, force the load at the linked address.
|
|
|
|
*/
|
|
|
|
if (*last == ELF_LINK_ADDR && (ph->p_vaddr & 0xffff0000) == 0)
|
|
|
|
*last = ELFDEFNNAME(NO_ADDR);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If no position to load the interpreter was set by a probe
|
|
|
|
* function, pick the same address that a non-fixed mmap(0, ..)
|
|
|
|
* would (i.e. something safely out of the way).
|
|
|
|
*/
|
|
|
|
if (*last == ELFDEFNNAME(NO_ADDR)) {
|
|
|
|
u_long limit = 0;
|
|
|
|
/*
|
|
|
|
* Find the start and ending addresses of the psections to
|
|
|
|
* be loaded. This will give us the size.
|
|
|
|
*/
|
2014-02-16 21:46:36 +04:00
|
|
|
for (i = 0, base_ph = NULL; i < eh.e_phnum; i++) {
|
|
|
|
if (ph[i].p_type == PT_LOAD) {
|
|
|
|
u_long psize = ph[i].p_vaddr + ph[i].p_memsz;
|
2008-11-19 21:35:57 +03:00
|
|
|
if (base_ph == NULL)
|
2014-02-16 21:46:36 +04:00
|
|
|
base_ph = &ph[i];
|
2008-11-19 21:35:57 +03:00
|
|
|
if (psize > limit)
|
|
|
|
limit = psize;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (base_ph == NULL) {
|
|
|
|
error = ENOEXEC;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now compute the size and load address.
|
|
|
|
*/
|
|
|
|
addr = (*epp->ep_esch->es_emul->e_vm_default_addr)(p,
|
|
|
|
epp->ep_daddr,
|
|
|
|
round_page(limit) - trunc_page(base_ph->p_vaddr));
|
|
|
|
} else
|
|
|
|
addr = *last; /* may be ELF_LINK_ADDR */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Load all the necessary sections
|
|
|
|
*/
|
2014-02-16 21:46:36 +04:00
|
|
|
for (i = 0, base_ph = NULL, last_ph = NULL; i < eh.e_phnum; i++) {
|
|
|
|
switch (ph[i].p_type) {
|
2008-11-19 21:35:57 +03:00
|
|
|
case PT_LOAD: {
|
|
|
|
u_long size;
|
|
|
|
int flags;
|
|
|
|
|
|
|
|
if (base_ph == NULL) {
|
|
|
|
/*
|
|
|
|
* First encountered psection is always the
|
|
|
|
* base psection. Make sure it's aligned
|
|
|
|
* properly (align down for topdown and align
|
|
|
|
* upwards for not topdown).
|
|
|
|
*/
|
2014-02-16 21:46:36 +04:00
|
|
|
base_ph = &ph[i];
|
2008-11-19 21:35:57 +03:00
|
|
|
flags = VMCMD_BASE;
|
|
|
|
if (addr == ELF_LINK_ADDR)
|
2014-02-16 21:46:36 +04:00
|
|
|
addr = ph[i].p_vaddr;
|
2012-02-12 03:16:15 +04:00
|
|
|
if (use_topdown)
|
2014-02-16 21:46:36 +04:00
|
|
|
addr = ELF_TRUNC(addr, ph[i].p_align);
|
2008-11-19 21:35:57 +03:00
|
|
|
else
|
2014-02-16 21:46:36 +04:00
|
|
|
addr = ELF_ROUND(addr, ph[i].p_align);
|
2008-11-19 21:35:57 +03:00
|
|
|
} else {
|
|
|
|
u_long limit = round_page(last_ph->p_vaddr
|
|
|
|
+ last_ph->p_memsz);
|
2014-02-16 21:46:36 +04:00
|
|
|
u_long base = trunc_page(ph[i].p_vaddr);
|
2008-11-19 21:35:57 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If there is a gap in between the psections,
|
|
|
|
* map it as inaccessible so nothing else
|
|
|
|
* mmap'ed will be placed there.
|
|
|
|
*/
|
|
|
|
if (limit != base) {
|
|
|
|
NEW_VMCMD2(vcset, vmcmd_map_zero,
|
|
|
|
base - limit,
|
|
|
|
limit - base_ph->p_vaddr, NULLVP,
|
|
|
|
0, VM_PROT_NONE, VMCMD_RELATIVE);
|
|
|
|
}
|
|
|
|
|
2014-02-16 21:46:36 +04:00
|
|
|
addr = ph[i].p_vaddr - base_ph->p_vaddr;
|
2008-11-19 21:35:57 +03:00
|
|
|
flags = VMCMD_RELATIVE;
|
|
|
|
}
|
2014-02-16 21:46:36 +04:00
|
|
|
last_ph = &ph[i];
|
2008-11-19 21:35:57 +03:00
|
|
|
elf_load_psection(vcset, vp, &ph[i], &addr,
|
2014-03-16 11:57:25 +04:00
|
|
|
&size, flags);
|
2008-11-19 21:35:57 +03:00
|
|
|
/*
|
|
|
|
* If entry is within this psection then this
|
|
|
|
* must contain the .text section. *entryoff is
|
|
|
|
* relative to the base psection.
|
|
|
|
*/
|
2014-02-16 21:46:36 +04:00
|
|
|
if (eh.e_entry >= ph[i].p_vaddr &&
|
|
|
|
eh.e_entry < (ph[i].p_vaddr + size)) {
|
2008-11-19 21:35:57 +03:00
|
|
|
*entryoff = eh.e_entry - base_ph->p_vaddr;
|
|
|
|
}
|
|
|
|
addr += size;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
kmem_free(ph, phsize);
|
|
|
|
/*
|
|
|
|
* This value is ignored if TOPDOWN.
|
|
|
|
*/
|
|
|
|
*last = addr;
|
|
|
|
vrele(vp);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
badunlock:
|
2010-06-24 16:58:48 +04:00
|
|
|
VOP_UNLOCK(vp);
|
2008-11-19 21:35:57 +03:00
|
|
|
|
|
|
|
bad:
|
|
|
|
if (ph != NULL)
|
|
|
|
kmem_free(ph, phsize);
|
|
|
|
vrele(vp);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* exec_elf_makecmds(): Prepare an Elf binary's exec package
|
|
|
|
*
|
|
|
|
* First, set of the various offsets/lengths in the exec package.
|
|
|
|
*
|
|
|
|
* Then, mark the text image busy (so it can be demand paged) or error
|
|
|
|
* out if this is not possible. Finally, set up vmcmds for the
|
|
|
|
* text, data, bss, and stack segments.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
exec_elf_makecmds(struct lwp *l, struct exec_package *epp)
|
|
|
|
{
|
|
|
|
Elf_Ehdr *eh = epp->ep_hdr;
|
|
|
|
Elf_Phdr *ph, *pp;
|
2010-09-12 00:49:28 +04:00
|
|
|
Elf_Addr phdr = 0, computed_phdr = 0, pos = 0, end_text = 0;
|
2014-02-16 21:46:36 +04:00
|
|
|
int error, i;
|
2008-11-19 21:35:57 +03:00
|
|
|
char *interp = NULL;
|
|
|
|
u_long phsize;
|
2014-03-22 11:27:21 +04:00
|
|
|
struct elf_args *ap;
|
2014-02-15 20:17:01 +04:00
|
|
|
bool is_dyn = false;
|
2008-11-19 21:35:57 +03:00
|
|
|
|
|
|
|
if (epp->ep_hdrvalid < sizeof(Elf_Ehdr))
|
|
|
|
return ENOEXEC;
|
2014-02-15 20:17:01 +04:00
|
|
|
if ((error = elf_check_header(eh)) != 0)
|
|
|
|
return error;
|
2008-11-19 21:35:57 +03:00
|
|
|
|
2014-02-15 20:17:01 +04:00
|
|
|
if (eh->e_type == ET_DYN)
|
2015-08-05 18:58:01 +03:00
|
|
|
/* PIE */
|
2014-02-15 20:17:01 +04:00
|
|
|
is_dyn = true;
|
|
|
|
else if (eh->e_type != ET_EXEC)
|
2008-11-19 21:35:57 +03:00
|
|
|
return ENOEXEC;
|
|
|
|
|
2013-08-26 16:24:10 +04:00
|
|
|
if (eh->e_phnum == 0)
|
2008-11-19 21:35:57 +03:00
|
|
|
return ENOEXEC;
|
|
|
|
|
|
|
|
error = vn_marktext(epp->ep_vp);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate space to hold all the program headers, and read them
|
|
|
|
* from the file
|
|
|
|
*/
|
|
|
|
phsize = eh->e_phnum * sizeof(Elf_Phdr);
|
|
|
|
ph = kmem_alloc(phsize, KM_SLEEP);
|
|
|
|
|
|
|
|
if ((error = exec_read_from(l, epp->ep_vp, eh->e_phoff, ph, phsize)) !=
|
|
|
|
0)
|
|
|
|
goto bad;
|
|
|
|
|
|
|
|
epp->ep_taddr = epp->ep_tsize = ELFDEFNNAME(NO_ADDR);
|
|
|
|
epp->ep_daddr = epp->ep_dsize = ELFDEFNNAME(NO_ADDR);
|
|
|
|
|
|
|
|
for (i = 0; i < eh->e_phnum; i++) {
|
|
|
|
pp = &ph[i];
|
|
|
|
if (pp->p_type == PT_INTERP) {
|
2013-12-21 18:41:02 +04:00
|
|
|
if (pp->p_filesz < 2 || pp->p_filesz > MAXPATHLEN) {
|
2008-11-19 21:35:57 +03:00
|
|
|
error = ENOEXEC;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
interp = PNBUF_GET();
|
2013-12-21 21:44:33 +04:00
|
|
|
if ((error = exec_read_from(l, epp->ep_vp,
|
|
|
|
pp->p_offset, interp, pp->p_filesz)) != 0)
|
|
|
|
goto bad;
|
2013-12-21 18:41:02 +04:00
|
|
|
/* Ensure interp is NUL-terminated and of the expected length */
|
|
|
|
if (strnlen(interp, pp->p_filesz) != pp->p_filesz - 1) {
|
|
|
|
error = ENOEXEC;
|
|
|
|
goto bad;
|
|
|
|
}
|
2008-11-19 21:35:57 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-05 18:58:01 +03:00
|
|
|
if (is_dyn && (interp == NULL)) {
|
|
|
|
/* PIEs must have an interpreter */
|
|
|
|
error = ENOEXEC;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
2008-11-19 21:35:57 +03:00
|
|
|
/*
|
|
|
|
* On the same architecture, we may be emulating different systems.
|
|
|
|
* See which one will accept this executable.
|
|
|
|
*
|
|
|
|
* Probe functions would normally see if the interpreter (if any)
|
|
|
|
* exists. Emulation packages may possibly replace the interpreter in
|
2015-08-04 21:28:09 +03:00
|
|
|
* interp with a changed path (/emul/xxx/<path>).
|
2008-11-19 21:35:57 +03:00
|
|
|
*/
|
|
|
|
pos = ELFDEFNNAME(NO_ADDR);
|
|
|
|
if (epp->ep_esch->u.elf_probe_func) {
|
|
|
|
vaddr_t startp = (vaddr_t)pos;
|
|
|
|
|
|
|
|
error = (*epp->ep_esch->u.elf_probe_func)(l, epp, eh, interp,
|
|
|
|
&startp);
|
|
|
|
if (error)
|
|
|
|
goto bad;
|
|
|
|
pos = (Elf_Addr)startp;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(PAX_MPROTECT) || defined(PAX_SEGVGUARD) || defined(PAX_ASLR)
|
Revamp PaX:
- don't confuse between ELF flags and proc flags. Introduce the proc-
specific P_PAX_ASLR, P_PAX_MPROTECT and P_PAX_GUARD flags.
- introduce pax_setup_elf_flags(), which takes as argument the PaX flag
of the ELF PaX note section, and which sets the proc flag as
appropriate. Also introduce a couple of other functions used for that
purpose.
- modify pax_aslr_active(), and all the other similar pieces of code, so
that it checks the proc flag directly, without extra ELF computation
In addition to making PaX clearer, the combination of these changes fixes
the following bug: if a non-PaX'ed process is launched, and then someone
sets security.pax.{aslr,mprotect,segvguard}.global=1, the process becomes
PaX'ed while its address space hasn't been randomized, which is not likely
to be a good idea.
Now, only the proc flag is checked at runtime, which means the process's
PaX status won't be altered during the execution.
Also:
- declare PAX_DPRINTF, makes it more readable
- fix a typo in exec_elf.h
2015-07-30 18:28:18 +03:00
|
|
|
pax_setup_elf_flags(l, epp->ep_pax_flags);
|
2008-11-19 21:35:57 +03:00
|
|
|
#endif /* PAX_MPROTECT || PAX_SEGVGUARD || PAX_ASLR */
|
|
|
|
|
|
|
|
if (is_dyn)
|
2010-03-20 04:45:30 +03:00
|
|
|
elf_placedynexec(l, epp, eh, ph);
|
2008-11-19 21:35:57 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Load all the necessary sections
|
|
|
|
*/
|
2014-02-16 21:46:36 +04:00
|
|
|
for (i = 0; i < eh->e_phnum; i++) {
|
|
|
|
Elf_Addr addr = ELFDEFNNAME(NO_ADDR);
|
2008-11-19 21:35:57 +03:00
|
|
|
u_long size = 0;
|
|
|
|
|
|
|
|
switch (ph[i].p_type) {
|
|
|
|
case PT_LOAD:
|
|
|
|
elf_load_psection(&epp->ep_vmcmds, epp->ep_vp,
|
2014-03-16 11:57:25 +04:00
|
|
|
&ph[i], &addr, &size, VMCMD_FIXED);
|
2008-11-19 21:35:57 +03:00
|
|
|
|
|
|
|
/*
|
2010-08-20 18:59:53 +04:00
|
|
|
* Consider this as text segment, if it is executable.
|
|
|
|
* If there is more than one text segment, pick the
|
|
|
|
* largest.
|
2008-11-19 21:35:57 +03:00
|
|
|
*/
|
2010-08-20 18:59:53 +04:00
|
|
|
if (ph[i].p_flags & PF_X) {
|
|
|
|
if (epp->ep_taddr == ELFDEFNNAME(NO_ADDR) ||
|
|
|
|
size > epp->ep_tsize) {
|
|
|
|
epp->ep_taddr = addr;
|
|
|
|
epp->ep_tsize = size;
|
2008-11-19 21:35:57 +03:00
|
|
|
}
|
2010-09-08 01:32:03 +04:00
|
|
|
end_text = addr + size;
|
2008-11-19 21:35:57 +03:00
|
|
|
} else {
|
|
|
|
epp->ep_daddr = addr;
|
|
|
|
epp->ep_dsize = size;
|
|
|
|
}
|
2010-09-12 00:49:28 +04:00
|
|
|
if (ph[i].p_offset == 0) {
|
|
|
|
computed_phdr = ph[i].p_vaddr + eh->e_phoff;
|
|
|
|
}
|
2008-11-19 21:35:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PT_SHLIB:
|
|
|
|
/* SCO has these sections. */
|
|
|
|
case PT_INTERP:
|
|
|
|
/* Already did this one. */
|
|
|
|
case PT_DYNAMIC:
|
|
|
|
case PT_NOTE:
|
|
|
|
break;
|
|
|
|
case PT_PHDR:
|
|
|
|
/* Note address of program headers (in text segment) */
|
2014-02-16 21:46:36 +04:00
|
|
|
phdr = ph[i].p_vaddr;
|
2008-11-19 21:35:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* Not fatal; we don't need to understand everything.
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-02-19 19:23:20 +04:00
|
|
|
|
2014-07-06 11:41:41 +04:00
|
|
|
if (epp->ep_vmcmds.evs_used == 0) {
|
2014-02-19 19:23:20 +04:00
|
|
|
/* No VMCMD; there was no PT_LOAD section, or those
|
|
|
|
* sections were empty */
|
|
|
|
error = ENOEXEC;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
2010-08-20 18:59:53 +04:00
|
|
|
if (epp->ep_daddr == ELFDEFNNAME(NO_ADDR)) {
|
2010-10-07 06:14:02 +04:00
|
|
|
epp->ep_daddr = round_page(end_text);
|
2010-09-08 01:32:03 +04:00
|
|
|
epp->ep_dsize = 0;
|
2010-08-20 18:59:53 +04:00
|
|
|
}
|
|
|
|
|
2008-11-19 21:35:57 +03:00
|
|
|
/*
|
|
|
|
* Check if we found a dynamically linked binary and arrange to load
|
|
|
|
* its interpreter
|
|
|
|
*/
|
|
|
|
if (interp) {
|
2014-03-22 11:27:21 +04:00
|
|
|
u_int nused = epp->ep_vmcmds.evs_used;
|
2014-02-15 21:39:03 +04:00
|
|
|
u_long interp_offset = 0;
|
2008-11-19 21:35:57 +03:00
|
|
|
|
2014-03-22 11:27:21 +04:00
|
|
|
if ((error = elf_load_interp(l, epp, interp,
|
2014-02-16 21:46:36 +04:00
|
|
|
&epp->ep_vmcmds, &interp_offset, &pos)) != 0) {
|
2008-11-19 21:35:57 +03:00
|
|
|
goto bad;
|
|
|
|
}
|
2014-02-19 19:23:20 +04:00
|
|
|
if (epp->ep_vmcmds.evs_used == nused) {
|
2014-03-22 11:27:21 +04:00
|
|
|
/* elf_load_interp() has not set up any new VMCMD */
|
2014-02-19 19:23:20 +04:00
|
|
|
error = ENOEXEC;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
2014-03-22 11:27:21 +04:00
|
|
|
ap = kmem_alloc(sizeof(*ap), KM_SLEEP);
|
2014-02-19 19:23:20 +04:00
|
|
|
ap->arg_interp = epp->ep_vmcmds.evs_cmds[nused].ev_addr;
|
2014-03-06 13:30:37 +04:00
|
|
|
epp->ep_entryoffset = interp_offset;
|
2008-11-19 21:35:57 +03:00
|
|
|
epp->ep_entry = ap->arg_interp + interp_offset;
|
2010-09-12 00:49:28 +04:00
|
|
|
PNBUF_PUT(interp);
|
2014-03-22 11:27:21 +04:00
|
|
|
} else {
|
2010-09-12 00:49:28 +04:00
|
|
|
epp->ep_entry = eh->e_entry;
|
2014-03-22 11:27:21 +04:00
|
|
|
if (epp->ep_flags & EXEC_FORCEAUX) {
|
|
|
|
ap = kmem_alloc(sizeof(*ap), KM_SLEEP);
|
|
|
|
ap->arg_interp = (vaddr_t)NULL;
|
|
|
|
} else
|
|
|
|
ap = NULL;
|
|
|
|
}
|
2008-11-19 21:35:57 +03:00
|
|
|
|
2010-09-12 00:49:28 +04:00
|
|
|
if (ap) {
|
|
|
|
ap->arg_phaddr = phdr ? phdr : computed_phdr;
|
2008-11-19 21:35:57 +03:00
|
|
|
ap->arg_phentsize = eh->e_phentsize;
|
|
|
|
ap->arg_phnum = eh->e_phnum;
|
|
|
|
ap->arg_entry = eh->e_entry;
|
|
|
|
epp->ep_emul_arg = ap;
|
2012-02-04 00:11:53 +04:00
|
|
|
epp->ep_emul_arg_free = elf_free_emul_arg;
|
2010-09-12 00:49:28 +04:00
|
|
|
}
|
2008-11-19 21:35:57 +03:00
|
|
|
|
|
|
|
#ifdef ELF_MAP_PAGE_ZERO
|
|
|
|
/* Dell SVR4 maps page zero, yeuch! */
|
|
|
|
NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_readvn, PAGE_SIZE, 0,
|
|
|
|
epp->ep_vp, 0, VM_PROT_READ);
|
|
|
|
#endif
|
|
|
|
kmem_free(ph, phsize);
|
|
|
|
return (*epp->ep_esch->es_setup_stack)(l, epp);
|
|
|
|
|
|
|
|
bad:
|
|
|
|
if (interp)
|
|
|
|
PNBUF_PUT(interp);
|
2012-02-04 00:11:53 +04:00
|
|
|
exec_free_emul_arg(epp);
|
2008-11-19 21:35:57 +03:00
|
|
|
kmem_free(ph, phsize);
|
|
|
|
kill_vmcmds(&epp->ep_vmcmds);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
netbsd_elf_signature(struct lwp *l, struct exec_package *epp,
|
|
|
|
Elf_Ehdr *eh)
|
|
|
|
{
|
|
|
|
size_t i;
|
2010-03-20 01:08:13 +03:00
|
|
|
Elf_Shdr *sh;
|
|
|
|
Elf_Nhdr *np;
|
2014-02-21 11:47:02 +04:00
|
|
|
size_t shsize, nsize;
|
2008-11-19 21:35:57 +03:00
|
|
|
int error;
|
|
|
|
int isnetbsd = 0;
|
2014-02-27 13:58:05 +04:00
|
|
|
char *ndata, *ndesc;
|
2014-06-25 21:10:39 +04:00
|
|
|
|
2014-05-15 23:37:22 +04:00
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
const char *badnote;
|
|
|
|
#define BADNOTE(n) badnote = (n)
|
|
|
|
#else
|
|
|
|
#define BADNOTE(n)
|
|
|
|
#endif
|
2008-11-19 21:35:57 +03:00
|
|
|
|
|
|
|
epp->ep_pax_flags = 0;
|
2014-07-08 21:16:25 +04:00
|
|
|
if (eh->e_shnum > ELF_MAXSHNUM || eh->e_shnum == 0)
|
2008-11-19 21:35:57 +03:00
|
|
|
return ENOEXEC;
|
|
|
|
|
2010-03-20 01:08:13 +03:00
|
|
|
shsize = eh->e_shnum * sizeof(Elf_Shdr);
|
|
|
|
sh = kmem_alloc(shsize, KM_SLEEP);
|
|
|
|
error = exec_read_from(l, epp->ep_vp, eh->e_shoff, sh, shsize);
|
2008-11-19 21:35:57 +03:00
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
|
2014-07-08 21:16:25 +04:00
|
|
|
np = kmem_alloc(ELF_MAXNOTESIZE, KM_SLEEP);
|
2010-03-20 01:08:13 +03:00
|
|
|
for (i = 0; i < eh->e_shnum; i++) {
|
|
|
|
Elf_Shdr *shp = &sh[i];
|
2008-11-19 21:35:57 +03:00
|
|
|
|
2010-03-20 01:08:13 +03:00
|
|
|
if (shp->sh_type != SHT_NOTE ||
|
2014-07-08 21:16:25 +04:00
|
|
|
shp->sh_size > ELF_MAXNOTESIZE ||
|
2010-03-20 01:08:13 +03:00
|
|
|
shp->sh_size < sizeof(Elf_Nhdr) + ELF_NOTE_NETBSD_NAMESZ)
|
2008-11-19 21:35:57 +03:00
|
|
|
continue;
|
|
|
|
|
2010-03-20 01:08:13 +03:00
|
|
|
error = exec_read_from(l, epp->ep_vp, shp->sh_offset, np,
|
|
|
|
shp->sh_size);
|
2008-11-19 21:35:57 +03:00
|
|
|
if (error)
|
2010-03-20 01:08:13 +03:00
|
|
|
continue;
|
2008-11-19 21:35:57 +03:00
|
|
|
|
2014-02-21 11:47:02 +04:00
|
|
|
/* Point to the note, skip the header */
|
2008-11-19 21:35:57 +03:00
|
|
|
ndata = (char *)(np + 1);
|
2014-02-21 11:47:02 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Padding is present if necessary to ensure 4-byte alignment.
|
|
|
|
* The actual section size is therefore:
|
|
|
|
* header size + 4-byte aligned name + 4-byte aligned desc
|
|
|
|
* Ensure this size is consistent with what is indicated
|
|
|
|
* in sh_size. The first check avoids integer overflows.
|
2014-08-18 03:03:58 +04:00
|
|
|
*
|
|
|
|
* Binaries from before NetBSD 1.6 have two notes in the same
|
|
|
|
* note section. The second note was never used, so as long as
|
|
|
|
* the section is at least as big as it should be, it's ok.
|
|
|
|
* These binaries also have a second note section with a note of
|
|
|
|
* type ELF_NOTE_TYPE_NETBSD_TAG, which can be ignored as well.
|
2014-02-21 11:47:02 +04:00
|
|
|
*/
|
2014-05-15 23:37:22 +04:00
|
|
|
if (np->n_namesz > shp->sh_size || np->n_descsz > shp->sh_size) {
|
|
|
|
BADNOTE("note size limit");
|
2012-05-22 06:40:05 +04:00
|
|
|
goto bad;
|
2014-05-15 23:37:22 +04:00
|
|
|
}
|
2014-02-21 11:47:02 +04:00
|
|
|
nsize = sizeof(*np) + roundup(np->n_namesz, 4) +
|
|
|
|
roundup(np->n_descsz, 4);
|
2014-08-18 03:03:58 +04:00
|
|
|
if (nsize > shp->sh_size) {
|
2014-05-15 23:37:22 +04:00
|
|
|
BADNOTE("note size");
|
2014-02-21 11:47:02 +04:00
|
|
|
goto bad;
|
2014-05-15 23:37:22 +04:00
|
|
|
}
|
2014-02-27 13:58:05 +04:00
|
|
|
ndesc = ndata + roundup(np->n_namesz, 4);
|
2014-02-21 11:47:02 +04:00
|
|
|
|
2008-11-19 21:35:57 +03:00
|
|
|
switch (np->n_type) {
|
|
|
|
case ELF_NOTE_TYPE_NETBSD_TAG:
|
2014-02-22 11:53:16 +04:00
|
|
|
/* It is us */
|
2012-05-22 06:40:05 +04:00
|
|
|
if (np->n_namesz == ELF_NOTE_NETBSD_NAMESZ &&
|
|
|
|
np->n_descsz == ELF_NOTE_NETBSD_DESCSZ &&
|
2008-11-19 21:35:57 +03:00
|
|
|
memcmp(ndata, ELF_NOTE_NETBSD_NAME,
|
2012-05-22 06:40:05 +04:00
|
|
|
ELF_NOTE_NETBSD_NAMESZ) == 0) {
|
2014-02-27 13:58:05 +04:00
|
|
|
memcpy(&epp->ep_osversion, ndesc,
|
2013-10-31 03:32:30 +04:00
|
|
|
ELF_NOTE_NETBSD_DESCSZ);
|
2012-05-22 06:40:05 +04:00
|
|
|
isnetbsd = 1;
|
|
|
|
break;
|
|
|
|
}
|
2014-02-22 11:53:16 +04:00
|
|
|
|
2012-05-22 06:40:05 +04:00
|
|
|
/*
|
2014-02-22 11:53:16 +04:00
|
|
|
* Ignore SuSE tags; SuSE's n_type is the same as NetBSD's
|
|
|
|
* one.
|
2012-05-22 06:40:05 +04:00
|
|
|
*/
|
|
|
|
if (np->n_namesz == ELF_NOTE_SUSE_NAMESZ &&
|
|
|
|
memcmp(ndata, ELF_NOTE_SUSE_NAME,
|
|
|
|
ELF_NOTE_SUSE_NAMESZ) == 0)
|
|
|
|
break;
|
2014-05-15 23:37:22 +04:00
|
|
|
BADNOTE("NetBSD tag");
|
2012-05-22 06:40:05 +04:00
|
|
|
goto bad;
|
2008-11-19 21:35:57 +03:00
|
|
|
|
|
|
|
case ELF_NOTE_TYPE_PAX_TAG:
|
2014-02-22 11:53:16 +04:00
|
|
|
if (np->n_namesz == ELF_NOTE_PAX_NAMESZ &&
|
|
|
|
np->n_descsz == ELF_NOTE_PAX_DESCSZ &&
|
2008-11-19 21:35:57 +03:00
|
|
|
memcmp(ndata, ELF_NOTE_PAX_NAME,
|
2014-02-22 11:53:16 +04:00
|
|
|
ELF_NOTE_PAX_NAMESZ) == 0) {
|
2014-02-27 13:58:05 +04:00
|
|
|
memcpy(&epp->ep_pax_flags, ndesc,
|
2014-02-22 11:53:16 +04:00
|
|
|
sizeof(epp->ep_pax_flags));
|
|
|
|
break;
|
2010-03-20 01:08:13 +03:00
|
|
|
}
|
2014-05-15 23:37:22 +04:00
|
|
|
BADNOTE("PaX tag");
|
2014-02-22 11:53:16 +04:00
|
|
|
goto bad;
|
2008-11-19 21:35:57 +03:00
|
|
|
|
2013-09-11 01:30:21 +04:00
|
|
|
case ELF_NOTE_TYPE_MARCH_TAG:
|
2014-02-22 11:53:16 +04:00
|
|
|
/* Copy the machine arch into the package. */
|
2013-09-11 01:30:21 +04:00
|
|
|
if (np->n_namesz == ELF_NOTE_MARCH_NAMESZ
|
|
|
|
&& memcmp(ndata, ELF_NOTE_MARCH_NAME,
|
|
|
|
ELF_NOTE_MARCH_NAMESZ) == 0) {
|
2014-02-27 13:58:05 +04:00
|
|
|
/* Do not truncate the buffer */
|
2014-05-15 23:37:22 +04:00
|
|
|
if (np->n_descsz > sizeof(epp->ep_machine_arch)) {
|
|
|
|
BADNOTE("description size limit");
|
2014-02-27 13:58:05 +04:00
|
|
|
goto bad;
|
2014-05-15 23:37:22 +04:00
|
|
|
}
|
2014-02-27 13:58:05 +04:00
|
|
|
/*
|
|
|
|
* Ensure ndesc is NUL-terminated and of the
|
|
|
|
* expected length.
|
|
|
|
*/
|
|
|
|
if (strnlen(ndesc, np->n_descsz) + 1 !=
|
2014-05-15 23:37:22 +04:00
|
|
|
np->n_descsz) {
|
|
|
|
BADNOTE("description size");
|
2014-02-27 13:58:05 +04:00
|
|
|
goto bad;
|
2014-05-15 23:37:22 +04:00
|
|
|
}
|
2014-02-27 13:58:05 +04:00
|
|
|
strlcpy(epp->ep_machine_arch, ndesc,
|
2013-09-11 01:30:21 +04:00
|
|
|
sizeof(epp->ep_machine_arch));
|
|
|
|
break;
|
|
|
|
}
|
2014-05-15 23:37:22 +04:00
|
|
|
BADNOTE("march tag");
|
2014-02-22 11:53:16 +04:00
|
|
|
goto bad;
|
|
|
|
|
2013-11-05 18:26:19 +04:00
|
|
|
case ELF_NOTE_TYPE_MCMODEL_TAG:
|
2014-02-22 11:53:16 +04:00
|
|
|
/* arch specific check for code model */
|
2013-11-05 18:26:19 +04:00
|
|
|
#ifdef ELF_MD_MCMODEL_CHECK
|
|
|
|
if (np->n_namesz == ELF_NOTE_MCMODEL_NAMESZ
|
|
|
|
&& memcmp(ndata, ELF_NOTE_MCMODEL_NAME,
|
|
|
|
ELF_NOTE_MCMODEL_NAMESZ) == 0) {
|
2014-02-27 13:58:05 +04:00
|
|
|
ELF_MD_MCMODEL_CHECK(epp, ndesc, np->n_descsz);
|
2013-11-05 18:26:19 +04:00
|
|
|
break;
|
|
|
|
}
|
2014-05-15 23:37:22 +04:00
|
|
|
BADNOTE("mcmodel tag");
|
2014-02-22 11:53:16 +04:00
|
|
|
goto bad;
|
2013-11-05 18:26:19 +04:00
|
|
|
#endif
|
|
|
|
break;
|
2013-09-11 01:30:21 +04:00
|
|
|
|
2012-05-22 06:40:05 +04:00
|
|
|
case ELF_NOTE_TYPE_SUSE_VERSION_TAG:
|
2011-08-02 20:44:01 +04:00
|
|
|
break;
|
|
|
|
|
2008-11-19 21:35:57 +03:00
|
|
|
default:
|
2014-05-15 23:37:22 +04:00
|
|
|
BADNOTE("unknown tag");
|
2014-02-22 11:53:16 +04:00
|
|
|
bad:
|
2010-03-20 01:08:13 +03:00
|
|
|
#ifdef DIAGNOSTIC
|
2014-02-22 11:53:16 +04:00
|
|
|
/* Ignore GNU tags */
|
|
|
|
if (np->n_namesz == ELF_NOTE_GNU_NAMESZ &&
|
|
|
|
memcmp(ndata, ELF_NOTE_GNU_NAME,
|
|
|
|
ELF_NOTE_GNU_NAMESZ) == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
int ns = MIN(np->n_namesz, shp->sh_size - sizeof(*np));
|
2014-05-15 23:37:22 +04:00
|
|
|
printf("%s: Unknown elf note type %d (%s): "
|
|
|
|
"[namesz=%d, descsz=%d name=%-*.*s]\n",
|
|
|
|
epp->ep_kname, np->n_type, badnote, np->n_namesz,
|
2014-02-22 11:53:16 +04:00
|
|
|
np->n_descsz, ns, ns, ndata);
|
2010-03-20 01:08:13 +03:00
|
|
|
#endif
|
2008-11-19 21:35:57 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-07-08 21:16:25 +04:00
|
|
|
kmem_free(np, ELF_MAXNOTESIZE);
|
2008-11-19 21:35:57 +03:00
|
|
|
|
|
|
|
error = isnetbsd ? 0 : ENOEXEC;
|
|
|
|
out:
|
2010-03-20 01:08:13 +03:00
|
|
|
kmem_free(sh, shsize);
|
2008-11-19 21:35:57 +03:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
netbsd_elf_probe(struct lwp *l, struct exec_package *epp, void *eh, char *itp,
|
|
|
|
vaddr_t *pos)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if ((error = netbsd_elf_signature(l, epp, eh)) != 0)
|
|
|
|
return error;
|
2009-12-14 03:47:10 +03:00
|
|
|
#ifdef ELF_MD_PROBE_FUNC
|
|
|
|
if ((error = ELF_MD_PROBE_FUNC(l, epp, eh, itp, pos)) != 0)
|
|
|
|
return error;
|
|
|
|
#elif defined(ELF_INTERP_NON_RELOCATABLE)
|
2008-11-19 21:35:57 +03:00
|
|
|
*pos = ELF_LINK_ADDR;
|
|
|
|
#endif
|
2011-03-07 08:09:09 +03:00
|
|
|
epp->ep_flags |= EXEC_FORCEAUX;
|
2008-11-19 21:35:57 +03:00
|
|
|
return 0;
|
|
|
|
}
|
2012-02-04 00:11:53 +04:00
|
|
|
|
|
|
|
void
|
|
|
|
elf_free_emul_arg(void *arg)
|
|
|
|
{
|
|
|
|
struct elf_args *ap = arg;
|
|
|
|
KASSERT(ap != NULL);
|
|
|
|
kmem_free(ap, sizeof(*ap));
|
|
|
|
}
|