summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorguenther <guenther@openbsd.org>2015-07-20 00:56:10 +0000
committerguenther <guenther@openbsd.org>2015-07-20 00:56:10 +0000
commit440e6fe6766bfd279b20c90368fedd8ecb875a9e (patch)
tree2390dc828ba63095f3436ee1bcdb0ccc82ec6297
parentTweak previous; the Synaptics TrackPoint in my ThinkPad T500 works again. (diff)
downloadwireguard-openbsd-440e6fe6766bfd279b20c90368fedd8ecb875a9e.tar.xz
wireguard-openbsd-440e6fe6766bfd279b20c90368fedd8ecb875a9e.zip
Add kbind, a syscall for ld.so to use to securely and efficiently update
memory for lazy binding ok deraadt@
-rw-r--r--sys/kern/kern_exec.c4
-rw-r--r--sys/kern/syscalls.master5
-rw-r--r--sys/sys/proc.h5
-rw-r--r--sys/sys/unistd.h10
-rw-r--r--sys/uvm/uvm_mmap.c129
5 files changed, 147 insertions, 6 deletions
diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c
index 0ff4c2cb126..f6b90f810fb 100644
--- a/sys/kern/kern_exec.c
+++ b/sys/kern/kern_exec.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_exec.c,v 1.161 2015/03/14 03:38:50 jsg Exp $ */
+/* $OpenBSD: kern_exec.c,v 1.162 2015/07/20 00:56:10 guenther Exp $ */
/* $NetBSD: kern_exec.c,v 1.75 1996/02/09 18:59:28 christos Exp $ */
/*-
@@ -499,6 +499,8 @@ sys_execve(struct proc *p, void *v, register_t *retval)
fdcloseexec(p); /* handle close on exec */
execsigs(p); /* reset caught signals */
TCB_SET(p, NULL); /* reset the TCB address */
+ pr->ps_kbind_addr = 0; /* reset the kbind bits */
+ pr->ps_kbind_cookie = 0;
/* set command name & other accounting info */
memset(p->p_comm, 0, sizeof(p->p_comm));
diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master
index aa713512e33..3ff01bd523c 100644
--- a/sys/kern/syscalls.master
+++ b/sys/kern/syscalls.master
@@ -1,4 +1,4 @@
-; $OpenBSD: syscalls.master,v 1.154 2015/07/19 02:35:35 deraadt Exp $
+; $OpenBSD: syscalls.master,v 1.155 2015/07/20 00:56:10 guenther Exp $
; $NetBSD: syscalls.master,v 1.32 1996/04/23 10:24:21 mycroft Exp $
; @(#)syscalls.master 8.2 (Berkeley) 1/13/94
@@ -191,7 +191,8 @@
const struct timespec *times, int flag); }
85 STD { int sys_futimens(int fd, \
const struct timespec *times); }
-86 OBSOL t32_getitimer
+86 STD { int sys_kbind(const struct __kbind *param, \
+ size_t psize, int64_t proc_cookie); }
87 STD NOLOCK { int sys_clock_gettime(clockid_t clock_id, \
struct timespec *tp); }
88 STD { int sys_clock_settime(clockid_t clock_id, \
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
index d212a6f3c40..f9473228bf8 100644
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: proc.h,v 1.201 2015/07/19 02:35:35 deraadt Exp $ */
+/* $OpenBSD: proc.h,v 1.202 2015/07/20 00:56:10 guenther Exp $ */
/* $NetBSD: proc.h,v 1.44 1996/04/22 01:23:21 christos Exp $ */
/*-
@@ -213,6 +213,9 @@ struct process {
u_int ps_tame;
+ int64_t ps_kbind_cookie;
+ u_long ps_kbind_addr;
+
/* End area that is copied on creation. */
#define ps_endcopy ps_refcnt
int ps_refcnt; /* Number of references. */
diff --git a/sys/sys/unistd.h b/sys/sys/unistd.h
index 46e4000c0e9..1b90fb1a03c 100644
--- a/sys/sys/unistd.h
+++ b/sys/sys/unistd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: unistd.h,v 1.30 2014/12/13 20:42:41 tedu Exp $ */
+/* $OpenBSD: unistd.h,v 1.31 2015/07/20 00:56:10 guenther Exp $ */
/* $NetBSD: unistd.h,v 1.10 1994/06/29 06:46:06 cgd Exp $ */
/*
@@ -68,6 +68,14 @@ struct __tfork {
pid_t *tf_tid;
void *tf_stack;
};
+
+/* the parameters argument for the kbind() syscall */
+struct __kbind {
+ void *kb_addr;
+ size_t kb_size;
+};
+#define KBIND_BLOCK_MAX 2 /* powerpc, sparc, and sparc64 need 2 blocks */
+#define KBIND_DATA_MAX 24 /* sparc64 needs 6, four-byte words */
#endif
/* the pathconf(2) variable values are part of the ABI */
diff --git a/sys/uvm/uvm_mmap.c b/sys/uvm/uvm_mmap.c
index bdb63e33713..cd4b7e76ad8 100644
--- a/sys/uvm/uvm_mmap.c
+++ b/sys/uvm/uvm_mmap.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: uvm_mmap.c,v 1.109 2015/05/07 08:53:33 mpi Exp $ */
+/* $OpenBSD: uvm_mmap.c,v 1.110 2015/07/20 00:56:10 guenther Exp $ */
/* $NetBSD: uvm_mmap.c,v 1.49 2001/02/18 21:19:08 chs Exp $ */
/*
@@ -61,9 +61,11 @@
#include <sys/malloc.h>
#include <sys/vnode.h>
#include <sys/conf.h>
+#include <sys/signalvar.h>
#include <sys/stat.h>
#include <sys/specdev.h>
#include <sys/stdint.h>
+#include <sys/unistd.h> /* for KBIND* */
#include <machine/exec.h> /* for __LDPGSZ */
@@ -1121,3 +1123,128 @@ uvm_mmapfile(vm_map_t map, vaddr_t *addr, vsize_t size, vm_prot_t prot,
return (error);
}
+
+/* an address that can't be in userspace */
+#define BOGO_PC (KERNBASE + 1)
+int
+sys_kbind(struct proc *p, void *v, register_t *retval)
+{
+#if defined(__vax__) || defined(__hppa64__)
+ /* only exists to support ld.so */
+ sigexit(p, SIGSYS);
+#else
+ struct sys_kbind_args /* {
+ syscallarg(const struct __kbind *) param;
+ syscallarg(size_t) psize;
+ syscallarg(uint64_t) proc_cookie;
+ } */ *uap = v;
+ const struct __kbind *paramp;
+ union {
+ struct __kbind uk[KBIND_BLOCK_MAX];
+ char upad[KBIND_BLOCK_MAX * sizeof(*paramp) + KBIND_DATA_MAX];
+ } param;
+ struct uvm_map_deadq dead_entries;
+ struct process *pr = p->p_p;
+ const char *data;
+ vaddr_t baseva, last_baseva, endva, pageoffset, kva;
+ size_t psize, s;
+ u_long pc;
+ int count, i;
+ int error;
+
+ /*
+ * extract syscall args from uap
+ */
+ paramp = SCARG(uap, param);
+ psize = SCARG(uap, psize);
+
+ /* a NULL paramp disables the syscall for the process */
+ if (paramp == NULL) {
+ pr->ps_kbind_addr = BOGO_PC;
+ return (0);
+ }
+
+ /* security checks */
+ pc = PROC_PC(p);
+ if (pr->ps_kbind_addr == 0) {
+ pr->ps_kbind_addr = pc;
+ pr->ps_kbind_cookie = SCARG(uap, proc_cookie);
+ } else if (pc != pr->ps_kbind_addr || pc == BOGO_PC)
+ sigexit(p, SIGILL);
+ else if (pr->ps_kbind_cookie != SCARG(uap, proc_cookie))
+ sigexit(p, SIGILL);
+ if (psize < sizeof(struct __kbind) || psize > sizeof(param))
+ return (EINVAL);
+ if ((error = copyin(paramp, &param, psize)))
+ return (error);
+
+ /*
+ * The param argument points to an array of __kbind structures
+ * followed by the corresponding new and old data areas for them
+ * in alternation. Verify that the sizes in the __kbind structures
+ * add up to the total size and find the start of the old+new area.
+ */
+ paramp = &param.uk[0];
+ s = psize;
+ for (count = 0; s > 0 && count < KBIND_BLOCK_MAX; count++) {
+ if (s < sizeof(*paramp))
+ return (EINVAL);
+ s -= sizeof(*paramp);
+
+ baseva = (vaddr_t)paramp[count].kb_addr;
+ endva = baseva + paramp[count].kb_size - 1;
+ if (paramp[count].kb_addr == NULL ||
+ paramp[count].kb_size == 0 ||
+ paramp[count].kb_size > KBIND_DATA_MAX ||
+ baseva >= VM_MAXUSER_ADDRESS ||
+ endva >= VM_MAXUSER_ADDRESS ||
+ trunc_page(baseva) != trunc_page(endva) ||
+ s < paramp[count].kb_size)
+ return (EINVAL);
+
+ s -= paramp[count].kb_size;
+ }
+ if (s > 0)
+ return (EINVAL);
+ data = (const char *)&paramp[count];
+
+ /* all looks good, so do the bindings */
+ last_baseva = VM_MAXUSER_ADDRESS;
+ kva = 0;
+ TAILQ_INIT(&dead_entries);
+ for (i = 0; i < count; i++) {
+ baseva = (vaddr_t)paramp[i].kb_addr;
+ pageoffset = baseva & PAGE_MASK;
+ baseva = trunc_page(baseva);
+
+ /* make sure sure the desired page is mapped into kernel_map */
+ if (baseva != last_baseva) {
+ if (kva != 0) {
+ vm_map_lock(kernel_map);
+ uvm_unmap_remove(kernel_map, kva,
+ kva+PAGE_SIZE, &dead_entries, FALSE, TRUE);
+ vm_map_unlock(kernel_map);
+ kva = 0;
+ }
+ if ((error = uvm_map_extract(&p->p_vmspace->vm_map,
+ baseva, PAGE_SIZE, &kva, UVM_EXTRACT_FIXPROT)))
+ break;
+ last_baseva = baseva;
+ }
+
+ /* do the update */
+ memcpy((char *)kva + pageoffset, data, paramp[i].kb_size);
+ data += paramp[i].kb_size;
+ }
+
+ if (kva != 0) {
+ vm_map_lock(kernel_map);
+ uvm_unmap_remove(kernel_map, kva, kva+PAGE_SIZE,
+ &dead_entries, FALSE, TRUE);
+ vm_map_unlock(kernel_map);
+ }
+ uvm_unmap_detach(&dead_entries, AMAP_REFALL);
+
+ return (error);
+#endif /* !vax && !hppa64 */
+}