diff options
author | 2015-07-20 00:56:10 +0000 | |
---|---|---|
committer | 2015-07-20 00:56:10 +0000 | |
commit | 440e6fe6766bfd279b20c90368fedd8ecb875a9e (patch) | |
tree | 2390dc828ba63095f3436ee1bcdb0ccc82ec6297 | |
parent | Tweak previous; the Synaptics TrackPoint in my ThinkPad T500 works again. (diff) | |
download | wireguard-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.c | 4 | ||||
-rw-r--r-- | sys/kern/syscalls.master | 5 | ||||
-rw-r--r-- | sys/sys/proc.h | 5 | ||||
-rw-r--r-- | sys/sys/unistd.h | 10 | ||||
-rw-r--r-- | sys/uvm/uvm_mmap.c | 129 |
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, ¶m, 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 = ¶m.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 *)¶mp[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 */ +} |