diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -1848,7 +1848,7 @@ * and is aligned at the end of the page, and the following page is not * mapped. */ - n = vmspace_iop(td, vm, (vm_offset_t)sptr, buf, len, UIO_READ); + n = vmspace_iop(td, vm, (vm_offset_t)sptr, buf, len, UIO_READ, VM_PROT_READ); if (n <= 0) return (ENOMEM); return (0); @@ -1877,7 +1877,7 @@ error = 0; if (vmspace_iop(td, vm, PROC_PS_STRINGS(p), &pss, sizeof(pss), - UIO_READ) != sizeof(pss)) + UIO_READ, VM_PROT_READ) != sizeof(pss)) return (ENOMEM); switch (type) { case PROC_ARG: @@ -1901,7 +1901,7 @@ return (ENOEXEC); for (ptr = vptr, i = 0; i < PROC_AUXV_MAX; i++) { if (vmspace_iop(td, vm, ptr, &aux, sizeof(aux), - UIO_READ) != sizeof(aux)) + UIO_READ, VM_PROT_READ) != sizeof(aux)) return (ENOMEM); if (aux.a_type == AT_NULL) break; @@ -1917,7 +1917,7 @@ return (EINVAL); } proc_vector32 = malloc(size, M_TEMP, M_WAITOK); - if (vmspace_iop(td, vm, vptr, proc_vector32, size, UIO_READ) != size) { + if (vmspace_iop(td, vm, vptr, proc_vector32, size, UIO_READ, VM_PROT_READ) != size) { error = ENOMEM; goto done; } @@ -1955,7 +1955,7 @@ } #endif if (vmspace_iop(td, vm, PROC_PS_STRINGS(p), &pss, sizeof(pss), - UIO_READ) != sizeof(pss)) + UIO_READ, VM_PROT_READ) != sizeof(pss)) return (ENOMEM); switch (type) { case PROC_ARG: @@ -1994,7 +1994,7 @@ */ for (ptr = vptr, i = 0; i < PROC_AUXV_MAX; i++) { if (vmspace_iop(td, vm, ptr, &aux, sizeof(aux), - UIO_READ) != sizeof(aux)) + UIO_READ, VM_PROT_READ) != sizeof(aux)) return (ENOMEM); if (aux.a_type == AT_NULL) break; @@ -2016,7 +2016,7 @@ return (EINVAL); /* In case we are built without INVARIANTS. */ } proc_vector = malloc(size, M_TEMP, M_WAITOK); - if (vmspace_iop(td, vm, vptr, proc_vector, size, UIO_READ) != size) { + if (vmspace_iop(td, vm, vptr, proc_vector, size, UIO_READ, VM_PROT_READ) != size) { free(proc_vector, M_TEMP); return (ENOMEM); } diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c --- a/sys/kern/sys_process.c +++ b/sys/kern/sys_process.c @@ -445,31 +445,26 @@ } } +/* + * Copy to/from the pages of a vmspace, faulting them in with the + * protection requested by the caller. Reads use VM_PROT_READ; writers + * choose between VM_PROT_COPY (a private copy, e.g. a debugger writing + * shared text) and VM_PROT_WRITE (a real, process-visible write, e.g. + * setting up a new process's stack). Any privilege policy is the + * caller's responsibility. + */ static int -vmspace_rwmem(struct vmspace *vm, struct uio *uio) +vmspace_rwmem(struct vmspace *vm, struct uio *uio, vm_prot_t reqprot) { vm_map_t map; vm_offset_t pageno; /* page number */ - vm_prot_t reqprot; int error, fault_flags, page_offset, writing; map = &vm->vm_map; - /* - * If we are writing, then we request vm_fault() to create a private - * copy of each page. Since these copies will not be writeable by the - * process, we must explicitly request that they be dirtied. - */ writing = uio->uio_rw == UIO_WRITE; - reqprot = writing ? VM_PROT_COPY | VM_PROT_READ : VM_PROT_READ; fault_flags = writing ? VM_FAULT_DIRTY : VM_FAULT_NORMAL; - if (writing) { - error = priv_check(curthread, PRIV_PROC_MEM_WRITE); - if (error != 0) - goto out; - } - /* * Only map in one page at a time. We don't have to, but it * makes things easier. This way is trivial - right? @@ -525,7 +520,6 @@ } while (error == 0 && uio->uio_resid > 0); -out: return (error); } @@ -537,17 +531,27 @@ int error; td = curthread; + /* + * Debugger-style access: a write goes to a private copy of the + * page (VM_PROT_COPY) and requires the memory-write privilege. + */ + if (uio->uio_rw == UIO_WRITE) { + error = priv_check(td, PRIV_PROC_MEM_WRITE); + if (error != 0) + return (error); + } error = proc_vmspace_ref(td, p, flags, &vm); if (error != 0) return (error); - error = vmspace_rwmem(vm, uio); + error = vmspace_rwmem(vm, uio, uio->uio_rw == UIO_WRITE ? + VM_PROT_COPY | VM_PROT_READ : VM_PROT_READ); proc_vmspace_unref(td, p, flags, vm); return (error); } ssize_t vmspace_iop(struct thread *td, struct vmspace *vm, vm_offset_t va, void *buf, - size_t len, enum uio_rw rw) + size_t len, enum uio_rw rw, vm_prot_t reqprot) { struct iovec iov; struct uio uio; @@ -566,7 +570,7 @@ uio.uio_segflg = UIO_SYSSPACE; uio.uio_rw = rw; uio.uio_td = td; - error = vmspace_rwmem(vm, &uio); + error = vmspace_rwmem(vm, &uio, reqprot); if (error != 0 || uio.uio_resid == slen) return (-1); return (slen - uio.uio_resid); @@ -577,15 +581,28 @@ size_t len) { - return (vmspace_iop(td, p->p_vmspace, va, buf, len, UIO_READ)); + return (vmspace_iop(td, p->p_vmspace, va, buf, len, UIO_READ, + VM_PROT_READ)); } ssize_t proc_writemem(struct thread *td, struct proc *p, vm_offset_t va, void *buf, size_t len) { + int error; - return (vmspace_iop(td, p->p_vmspace, va, buf, len, UIO_WRITE)); + /* + * Debugger-style write: private copy, memory-write privilege. + * The privilege is the acting thread's, which is always curthread + * (priv_check() asserts as much); td is merely the fault context + * passed to vmspace_iop() and may be the target thread (e.g. arm + * ptrace single-step calls proc_writemem() on the traced thread). + */ + error = priv_check(curthread, PRIV_PROC_MEM_WRITE); + if (error != 0) + return (-1); + return (vmspace_iop(td, p->p_vmspace, va, buf, len, UIO_WRITE, + VM_PROT_COPY | VM_PROT_READ)); } static int diff --git a/sys/sys/ptrace.h b/sys/sys/ptrace.h --- a/sys/sys/ptrace.h +++ b/sys/sys/ptrace.h @@ -266,13 +266,14 @@ #define PRVM_CHECK_DEBUG 0x00000004 #include +#include /* vm_prot_t */ struct vmspace; int proc_vmspace_ref(struct thread *_td, struct proc *_p, int _flags, struct vmspace **_vmp); void proc_vmspace_unref(struct thread *_td, struct proc *_p, int _flags, struct vmspace *_vm); ssize_t vmspace_iop(struct thread *td, struct vmspace *vm, vm_offset_t va, - void *buf, size_t len, enum uio_rw rw); + void *buf, size_t len, enum uio_rw rw, vm_prot_t reqprot); int proc_rwmem(struct proc *_p, struct uio *_uio, int _flags); ssize_t proc_readmem(struct thread *_td, struct proc *_p, vm_offset_t _va, void *_buf, size_t _len);