summaryrefslogtreecommitdiffstats
path: root/sys/compat/linux
diff options
context:
space:
mode:
Diffstat (limited to 'sys/compat/linux')
-rw-r--r--sys/compat/linux/linux_emul.c17
-rw-r--r--sys/compat/linux/linux_emul.h1
-rw-r--r--sys/compat/linux/linux_misc.c18
-rw-r--r--sys/compat/linux/linux_mmap.c257
-rw-r--r--sys/compat/linux/linux_mmap.h49
-rw-r--r--sys/compat/linux/linux_persona.h56
6 files changed, 392 insertions, 6 deletions
diff --git a/sys/compat/linux/linux_emul.c b/sys/compat/linux/linux_emul.c
index c2bf3ae..b244eea 100644
--- a/sys/compat/linux/linux_emul.c
+++ b/sys/compat/linux/linux_emul.c
@@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$");
#include <compat/linux/linux_emul.h>
#include <compat/linux/linux_misc.h>
+#include <compat/linux/linux_persona.h>
#include <compat/linux/linux_util.h>
@@ -127,7 +128,7 @@ linux_proc_init(struct thread *td, struct thread *newtd, int flags)
/* epoll should be destroyed in a case of exec. */
pem = pem_find(p);
KASSERT(pem != NULL, ("proc_exit: proc emuldata not found.\n"));
-
+ pem->persona = 0;
if (pem->epoll != NULL) {
emd = pem->epoll;
pem->epoll = NULL;
@@ -220,6 +221,9 @@ linux_proc_exec(void *arg __unused, struct proc *p, struct image_params *imgp)
{
struct thread *td = curthread;
struct thread *othertd;
+#if defined(__amd64__)
+ struct linux_pemuldata *pem;
+#endif
/*
* In a case of execing from linux binary properly detach
@@ -243,6 +247,17 @@ linux_proc_exec(void *arg __unused, struct proc *p, struct image_params *imgp)
linux_proc_init(td, NULL, 0);
else
linux_proc_init(td, td, 0);
+#if defined(__amd64__)
+ /*
+ * An IA32 executable which has executable stack will have the
+ * READ_IMPLIES_EXEC personality flag set automatically.
+ */
+ if (SV_PROC_FLAG(td->td_proc, SV_ILP32) &&
+ imgp->stack_prot & VM_PROT_EXECUTE) {
+ pem = pem_find(p);
+ pem->persona |= LINUX_READ_IMPLIES_EXEC;
+ }
+#endif
}
}
diff --git a/sys/compat/linux/linux_emul.h b/sys/compat/linux/linux_emul.h
index 7262093..9a5a667 100644
--- a/sys/compat/linux/linux_emul.h
+++ b/sys/compat/linux/linux_emul.h
@@ -67,6 +67,7 @@ struct linux_pemuldata {
uint32_t flags; /* process emuldata flags */
struct sx pem_sx; /* lock for this struct */
void *epoll; /* epoll data */
+ uint32_t persona; /* process execution domain */
};
#define LINUX_PEM_XLOCK(p) sx_xlock(&(p)->pem_sx)
diff --git a/sys/compat/linux/linux_misc.c b/sys/compat/linux/linux_misc.c
index cb2379a5..2765dfd 100644
--- a/sys/compat/linux/linux_misc.c
+++ b/sys/compat/linux/linux_misc.c
@@ -1198,15 +1198,23 @@ linux_mknodat(struct thread *td, struct linux_mknodat_args *args)
int
linux_personality(struct thread *td, struct linux_personality_args *args)
{
+ struct linux_pemuldata *pem;
+ struct proc *p = td->td_proc;
+ uint32_t old;
+
#ifdef DEBUG
if (ldebug(personality))
- printf(ARGS(personality, "%lu"), (unsigned long)args->per);
+ printf(ARGS(personality, "%u"), args->per);
#endif
- if (args->per != 0)
- return (EINVAL);
- /* Yes Jim, it's still a Linux... */
- td->td_retval[0] = 0;
+ PROC_LOCK(p);
+ pem = pem_find(p);
+ old = pem->persona;
+ if (args->per != 0xffffffff)
+ pem->persona = args->per;
+ PROC_UNLOCK(p);
+
+ td->td_retval[0] = old;
return (0);
}
diff --git a/sys/compat/linux/linux_mmap.c b/sys/compat/linux/linux_mmap.c
new file mode 100644
index 0000000..0a5557b
--- /dev/null
+++ b/sys/compat/linux/linux_mmap.c
@@ -0,0 +1,257 @@
+/*-
+ * Copyright (c) 2004 Tim J. Robbins
+ * Copyright (c) 2002 Doug Rabson
+ * Copyright (c) 2000 Marcel Moolenaar
+ * Copyright (c) 1994-1995 Søren Schmidt
+ * 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
+ * in this position and unchanged.
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/capsicum.h>
+#include <sys/file.h>
+#include <sys/imgact.h>
+#include <sys/ktr.h>
+#include <sys/mman.h>
+#include <sys/proc.h>
+#include <sys/resourcevar.h>
+#include <sys/sysent.h>
+#include <sys/sysproto.h>
+
+#include <vm/pmap.h>
+#include <vm/vm_map.h>
+
+#include <compat/linux/linux_emul.h>
+#include <compat/linux/linux_mmap.h>
+#include <compat/linux/linux_persona.h>
+#include <compat/linux/linux_util.h>
+
+
+#define STACK_SIZE (2 * 1024 * 1024)
+#define GUARD_SIZE (4 * PAGE_SIZE)
+
+#if defined(__amd64__)
+static void linux_fixup_prot(struct thread *td, int *prot);
+#endif
+
+
+int
+linux_mmap_common(struct thread *td, uintptr_t addr, size_t len, int prot,
+ int flags, int fd, off_t pos)
+{
+ struct proc *p = td->td_proc;
+ struct vmspace *vms = td->td_proc->p_vmspace;
+ struct mmap_args /* {
+ caddr_t addr;
+ size_t len;
+ int prot;
+ int flags;
+ int fd;
+ off_t pos;
+ } */ bsd_args;
+ int error;
+ struct file *fp;
+
+ cap_rights_t rights;
+ LINUX_CTR6(mmap2, "0x%lx, %ld, %ld, 0x%08lx, %ld, 0x%lx",
+ addr, len, prot, flags, fd, pos);
+
+ error = 0;
+ bsd_args.flags = 0;
+ fp = NULL;
+
+ /*
+ * Linux mmap(2):
+ * You must specify exactly one of MAP_SHARED and MAP_PRIVATE
+ */
+ if (!((flags & LINUX_MAP_SHARED) ^ (flags & LINUX_MAP_PRIVATE)))
+ return (EINVAL);
+
+ if (flags & LINUX_MAP_SHARED)
+ bsd_args.flags |= MAP_SHARED;
+ if (flags & LINUX_MAP_PRIVATE)
+ bsd_args.flags |= MAP_PRIVATE;
+ if (flags & LINUX_MAP_FIXED)
+ bsd_args.flags |= MAP_FIXED;
+ if (flags & LINUX_MAP_ANON) {
+ /* Enforce pos to be on page boundary, then ignore. */
+ if ((pos & PAGE_MASK) != 0)
+ return (EINVAL);
+ pos = 0;
+ bsd_args.flags |= MAP_ANON;
+ } else
+ bsd_args.flags |= MAP_NOSYNC;
+ if (flags & LINUX_MAP_GROWSDOWN)
+ bsd_args.flags |= MAP_STACK;
+
+ /*
+ * PROT_READ, PROT_WRITE, or PROT_EXEC implies PROT_READ and PROT_EXEC
+ * on Linux/i386 if the binary requires executable stack.
+ * We do this only for IA32 emulation as on native i386 this is does not
+ * make sense without PAE.
+ *
+ * XXX. Linux checks that the file system is not mounted with noexec.
+ */
+ bsd_args.prot = prot;
+#if defined(__amd64__)
+ linux_fixup_prot(td, &bsd_args.prot);
+#endif
+
+ /* Linux does not check file descriptor when MAP_ANONYMOUS is set. */
+ bsd_args.fd = (bsd_args.flags & MAP_ANON) ? -1 : fd;
+ if (bsd_args.fd != -1) {
+ /*
+ * Linux follows Solaris mmap(2) description:
+ * The file descriptor fildes is opened with
+ * read permission, regardless of the
+ * protection options specified.
+ */
+
+ error = fget(td, bsd_args.fd,
+ cap_rights_init(&rights, CAP_MMAP), &fp);
+ if (error != 0)
+ return (error);
+ if (fp->f_type != DTYPE_VNODE) {
+ fdrop(fp, td);
+ return (EINVAL);
+ }
+
+ /* Linux mmap() just fails for O_WRONLY files */
+ if (!(fp->f_flag & FREAD)) {
+ fdrop(fp, td);
+ return (EACCES);
+ }
+
+ fdrop(fp, td);
+ }
+
+ if (flags & LINUX_MAP_GROWSDOWN) {
+ /*
+ * The Linux MAP_GROWSDOWN option does not limit auto
+ * growth of the region. Linux mmap with this option
+ * takes as addr the initial BOS, and as len, the initial
+ * region size. It can then grow down from addr without
+ * limit. However, Linux threads has an implicit internal
+ * limit to stack size of STACK_SIZE. Its just not
+ * enforced explicitly in Linux. But, here we impose
+ * a limit of (STACK_SIZE - GUARD_SIZE) on the stack
+ * region, since we can do this with our mmap.
+ *
+ * Our mmap with MAP_STACK takes addr as the maximum
+ * downsize limit on BOS, and as len the max size of
+ * the region. It then maps the top SGROWSIZ bytes,
+ * and auto grows the region down, up to the limit
+ * in addr.
+ *
+ * If we don't use the MAP_STACK option, the effect
+ * of this code is to allocate a stack region of a
+ * fixed size of (STACK_SIZE - GUARD_SIZE).
+ */
+
+ if ((caddr_t)addr + len > vms->vm_maxsaddr) {
+ /*
+ * Some Linux apps will attempt to mmap
+ * thread stacks near the top of their
+ * address space. If their TOS is greater
+ * than vm_maxsaddr, vm_map_growstack()
+ * will confuse the thread stack with the
+ * process stack and deliver a SEGV if they
+ * attempt to grow the thread stack past their
+ * current stacksize rlimit. To avoid this,
+ * adjust vm_maxsaddr upwards to reflect
+ * the current stacksize rlimit rather
+ * than the maximum possible stacksize.
+ * It would be better to adjust the
+ * mmap'ed region, but some apps do not check
+ * mmap's return value.
+ */
+ PROC_LOCK(p);
+ vms->vm_maxsaddr = (char *)p->p_sysent->sv_usrstack -
+ lim_cur(p, RLIMIT_STACK);
+ PROC_UNLOCK(p);
+ }
+
+ /*
+ * This gives us our maximum stack size and a new BOS.
+ * If we're using VM_STACK, then mmap will just map
+ * the top SGROWSIZ bytes, and let the stack grow down
+ * to the limit at BOS. If we're not using VM_STACK
+ * we map the full stack, since we don't have a way
+ * to autogrow it.
+ */
+ if (len > STACK_SIZE - GUARD_SIZE) {
+ bsd_args.addr = (caddr_t)addr;
+ bsd_args.len = len;
+ } else {
+ bsd_args.addr = (caddr_t)addr -
+ (STACK_SIZE - GUARD_SIZE - len);
+ bsd_args.len = STACK_SIZE - GUARD_SIZE;
+ }
+ } else {
+ bsd_args.addr = (caddr_t)addr;
+ bsd_args.len = len;
+ }
+ bsd_args.pos = pos;
+
+ error = sys_mmap(td, &bsd_args);
+
+ LINUX_CTR2(mmap2, "return: %d (%p)", error, td->td_retval[0]);
+
+ return (error);
+}
+
+int
+linux_mprotect_common(struct thread *td, uintptr_t addr, size_t len, int prot)
+{
+ struct mprotect_args bsd_args;
+
+ bsd_args.addr = (void *)addr;
+ bsd_args.len = len;
+ bsd_args.prot = prot;
+
+#if defined(__amd64__)
+ linux_fixup_prot(td, &bsd_args.prot);
+#endif
+ return (sys_mprotect(td, &bsd_args));
+}
+
+#if defined(__amd64__)
+static void
+linux_fixup_prot(struct thread *td, int *prot)
+{
+ struct linux_pemuldata *pem;
+
+ if (SV_PROC_FLAG(td->td_proc, SV_ILP32) && *prot & PROT_READ) {
+ pem = pem_find(td->td_proc);
+ if (pem->persona & LINUX_READ_IMPLIES_EXEC)
+ *prot |= PROT_EXEC;
+ }
+
+}
+#endif
diff --git a/sys/compat/linux/linux_mmap.h b/sys/compat/linux/linux_mmap.h
new file mode 100644
index 0000000..a27d99d
--- /dev/null
+++ b/sys/compat/linux/linux_mmap.h
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2004 Tim J. Robbins
+ * Copyright (c) 2002 Doug Rabson
+ * Copyright (c) 2000 Marcel Moolenaar
+ * Copyright (c) 1994-1995 Søren Schmidt
+ * 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
+ * in this position and unchanged.
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _LINUX_MMAP_H_
+#define _LINUX_MMAP_H_
+
+/* mmap options */
+#define LINUX_MAP_SHARED 0x0001
+#define LINUX_MAP_PRIVATE 0x0002
+#define LINUX_MAP_FIXED 0x0010
+#define LINUX_MAP_ANON 0x0020
+#define LINUX_MAP_GROWSDOWN 0x0100
+
+
+int linux_mmap_common(struct thread *, uintptr_t, size_t, int, int,
+ int, off_t);
+int linux_mprotect_common(struct thread *, uintptr_t, size_t, int);
+
+#endif /* _LINUX_MMAP_H_ */
diff --git a/sys/compat/linux/linux_persona.h b/sys/compat/linux/linux_persona.h
new file mode 100644
index 0000000..8d84134
--- /dev/null
+++ b/sys/compat/linux/linux_persona.h
@@ -0,0 +1,56 @@
+/*
+ * $FreeBSD$
+ */
+
+#ifndef LINUX_PERSONALITY_H
+#define LINUX_PERSONALITY_H
+
+/*
+ * Flags for bug emulation.
+ *
+ * These occupy the top three bytes.
+ */
+enum {
+ LINUX_UNAME26 = 0x0020000,
+ LINUX_ADDR_NO_RANDOMIZE = 0x0040000, /* disable randomization
+ * of VA space
+ */
+ LINUX_FDPIC_FUNCPTRS = 0x0080000, /* userspace function
+ * ptrs point to descriptors
+ * (signal handling)
+ */
+ LINUX_MMAP_PAGE_ZERO = 0x0100000,
+ LINUX_ADDR_COMPAT_LAYOUT = 0x0200000,
+ LINUX_READ_IMPLIES_EXEC = 0x0400000,
+ LINUX_ADDR_LIMIT_32BIT = 0x0800000,
+ LINUX_SHORT_INODE = 0x1000000,
+ LINUX_WHOLE_SECONDS = 0x2000000,
+ LINUX_STICKY_TIMEOUTS = 0x4000000,
+ LINUX_ADDR_LIMIT_3GB = 0x8000000,
+};
+
+/*
+ * Security-relevant compatibility flags that must be
+ * cleared upon setuid or setgid exec:
+ */
+#define LINUX_PER_CLEAR_ON_SETID (LINUX_READ_IMPLIES_EXEC | \
+ LINUX_ADDR_NO_RANDOMIZE | \
+ LINUX_ADDR_COMPAT_LAYOUT | \
+ LINUX_MMAP_PAGE_ZERO)
+
+/*
+ * Personality types.
+ *
+ * These go in the low byte. Avoid using the top bit, it will
+ * conflict with error returns.
+ */
+enum {
+ LINUX_PER_LINUX = 0x0000,
+ LINUX_PER_LINUX_32BIT = 0x0000 | LINUX_ADDR_LIMIT_32BIT,
+ LINUX_PER_LINUX_FDPIC = 0x0000 | LINUX_FDPIC_FUNCPTRS,
+ LINUX_PER_LINUX32 = 0x0008,
+ LINUX_PER_LINUX32_3GB = 0x0008 | LINUX_ADDR_LIMIT_3GB,
+ LINUX_PER_MASK = 0x00ff,
+};
+
+#endif /* LINUX_PERSONALITY_H */
OpenPOWER on IntegriCloud