Index: share/man/man9/Makefile =================================================================== --- share/man/man9/Makefile +++ share/man/man9/Makefile @@ -239,6 +239,7 @@ printf.9 \ prison_check.9 \ priv.9 \ + proc_rwmem.9 \ pseudofs.9 \ psignal.9 \ random.9 \ @@ -1339,6 +1340,8 @@ printf.9 uprintf.9 MLINKS+=priv.9 priv_check.9 \ priv.9 priv_check_cred.9 +MLINKS+=proc_rwmem.9 proc_readmem.9 \ + proc_rwmem.9 proc_writemem.9 MLINKS+=psignal.9 gsignal.9 \ psignal.9 pgsignal.9 \ psignal.9 tdsignal.9 Index: share/man/man9/proc_rwmem.9 =================================================================== --- /dev/null +++ share/man/man9/proc_rwmem.9 @@ -0,0 +1,110 @@ +.\" +.\" Copyright (c) 2015 Mark Johnston +.\" +.\" 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 DEVELOPERS ``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 DEVELOPERS 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$ +.\" +.Dd December 4, 2015 +.Dt PROC_RWMEM 9 +.Os +.Sh NAME +.Nm proc_rwmem , +.Nm proc_readmem , +.Nm proc_writemem +.Nd read from or write to a process address space +.Sh SYNOPSIS +.In sys/types.h +.In sys/ptrace.h +.Ft int +.Fn proc_rwmem "struct proc *p" "struct uio *uio" +.Ft ssize_t +.Fo proc_readmem +.Fa "struct thread *td" "struct proc *p" "vm_offset_t va" "void *buf" +.Fa "size_t len" +.Fc +.Ft ssize_t +.Fo proc_writemem +.Fa "struct thread *td" "struct proc *p" "vm_offset_t va" "void *buf" +.Fa "size_t len" +.Fc +.Sh DESCRIPTION +These functions are used to read to or write from the address space of the +process +.Fa p . +The +.Fn proc_rwmem +function requires the caller to specify the I/O parameters using a +.Vt "struct uio" , +described in +.Xr uio 9 . +The +.Fn proc_readmem +and +.Fn proc_writemem +functions provide a simpler, less general interface which allows the caller to +read into or write the kernel buffer +.Fa buf +of size +.Fa len +from or to the memory at offset +.Fa va +in the address space of +.Fa p . +The operation is performed on behalf of thread +.Fa td , +which will most often be the current thread. +.Pp +These functions may sleep and thus may not be called with any non-sleepable +locks held. +The process +.Fa p +must be held by the caller using +.Xr PHOLD 9 . +.Sh RETURN VALUES +The +.Fn proc_rwmem +function returns +.Dv 0 +on success. +.Dv EFAULT +is returned if the specified user address is invalid, and +.Dv ENOMEM +is returned if the target pages could not be faulted in due to a resource +shortage. +.Pp +The +.Fn proc_readmem +and +.Fn proc_writemem +functions return the number of bytes read or written, respectively. +This may be smaller than the number of bytes requested, for example if the +request spans multiple pages in the process address space and one of them after +the first is not mapped. +Otherwise, -1 is returned. +.Sh SEE ALSO +.Xr copyin 9 , +.Xr locking 9 , +.Xr PHOLD 9 , +.Xr uio 9 +.Sh AUTHORS +This manual page was written by +.An Mark Johnston Aq Mt markj@FreeBSD.org . Index: sys/arm/arm/machdep.c =================================================================== --- sys/arm/arm/machdep.c +++ sys/arm/arm/machdep.c @@ -591,41 +591,21 @@ static int -ptrace_read_int(struct thread *td, vm_offset_t addr, u_int32_t *v) +ptrace_read_int(struct thread *td, vm_offset_t addr, uint32_t *v) { - struct iovec iov; - struct uio uio; - - PROC_LOCK_ASSERT(td->td_proc, MA_NOTOWNED); - iov.iov_base = (caddr_t) v; - iov.iov_len = sizeof(u_int32_t); - uio.uio_iov = &iov; - uio.uio_iovcnt = 1; - uio.uio_offset = (off_t)addr; - uio.uio_resid = sizeof(u_int32_t); - uio.uio_segflg = UIO_SYSSPACE; - uio.uio_rw = UIO_READ; - uio.uio_td = td; - return proc_rwmem(td->td_proc, &uio); + + if (proc_readmem(td, td->td_proc, addr, v, sizeof(*v)) != sizeof(*v)) + return (ENOMEM); + return (0); } static int -ptrace_write_int(struct thread *td, vm_offset_t addr, u_int32_t v) +ptrace_write_int(struct thread *td, vm_offset_t addr, uint32_t v) { - struct iovec iov; - struct uio uio; - - PROC_LOCK_ASSERT(td->td_proc, MA_NOTOWNED); - iov.iov_base = (caddr_t) &v; - iov.iov_len = sizeof(u_int32_t); - uio.uio_iov = &iov; - uio.uio_iovcnt = 1; - uio.uio_offset = (off_t)addr; - uio.uio_resid = sizeof(u_int32_t); - uio.uio_segflg = UIO_SYSSPACE; - uio.uio_rw = UIO_WRITE; - uio.uio_td = td; - return proc_rwmem(td->td_proc, &uio); + + if (proc_writemem(td, td->td_proc, addr, &v, sizeof(v)) != sizeof(v)) + return (ENOMEM); + return (0); } static u_int Index: sys/cddl/contrib/opensolaris/uts/intel/dtrace/fasttrap_isa.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/intel/dtrace/fasttrap_isa.c +++ sys/cddl/contrib/opensolaris/uts/intel/dtrace/fasttrap_isa.c @@ -60,43 +60,31 @@ #include static int -proc_ops(int op, proc_t *p, void *kaddr, off_t uaddr, size_t len) +uread(proc_t *p, void *kaddr, size_t len, uintptr_t uaddr) { - struct iovec iov; - struct uio uio; - - iov.iov_base = kaddr; - iov.iov_len = len; - uio.uio_offset = uaddr; - uio.uio_iov = &iov; - uio.uio_resid = len; - uio.uio_iovcnt = 1; - uio.uio_segflg = UIO_SYSSPACE; - uio.uio_td = curthread; - uio.uio_rw = op; + ssize_t n; + PHOLD(p); - if (proc_rwmem(p, &uio) != 0) { - PRELE(p); - return (-1); - } + n = proc_readmem(curthread, p, uaddr, kaddr, len); PRELE(p); - + if (n != len) + return (ENOMEM); return (0); } static int -uread(proc_t *p, void *kaddr, size_t len, uintptr_t uaddr) -{ - - return (proc_ops(UIO_READ, p, kaddr, uaddr, len)); -} - -static int uwrite(proc_t *p, void *kaddr, size_t len, uintptr_t uaddr) { + ssize_t n; - return (proc_ops(UIO_WRITE, p, kaddr, uaddr, len)); + PHOLD(p); + n = proc_writemem(curthread, p, uaddr, kaddr, len); + PRELE(p); + if (n != len) + return (ENOMEM); + return (0); } + #endif /* illumos */ #ifdef __i386__ #define r_rax r_eax Index: sys/cddl/contrib/opensolaris/uts/powerpc/dtrace/fasttrap_isa.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/powerpc/dtrace/fasttrap_isa.c +++ sys/cddl/contrib/opensolaris/uts/powerpc/dtrace/fasttrap_isa.c @@ -43,44 +43,30 @@ #define OP_RA(x) (((x) & 0x001F0000) >> 16) #define OP_RB(x) (((x) & 0x0000F100) >> 11) - static int -proc_ops(int op, proc_t *p, void *kaddr, off_t uaddr, size_t len) +uread(proc_t *p, void *kaddr, size_t len, uintptr_t uaddr) { - struct iovec iov; - struct uio uio; - - iov.iov_base = kaddr; - iov.iov_len = len; - uio.uio_offset = uaddr; - uio.uio_iov = &iov; - uio.uio_resid = len; - uio.uio_iovcnt = 1; - uio.uio_segflg = UIO_SYSSPACE; - uio.uio_td = curthread; - uio.uio_rw = op; + ssize_t n; + PHOLD(p); - if (proc_rwmem(p, &uio) != 0) { - PRELE(p); - return (-1); - } + n = proc_readmem(curthread, p, uaddr, kaddr, len); PRELE(p); - + if (n <= 0 || n < len) + return (ENOMEM); return (0); } static int -uread(proc_t *p, void *kaddr, size_t len, uintptr_t uaddr) -{ - - return (proc_ops(UIO_READ, p, kaddr, uaddr, len)); -} - -static int uwrite(proc_t *p, void *kaddr, size_t len, uintptr_t uaddr) { + ssize_t n; - return (proc_ops(UIO_WRITE, p, kaddr, uaddr, len)); + PHOLD(p); + n = proc_writemem(curthread, p, uaddr, kaddr, len); + PRELE(p); + if (n <= 0 || n < len) + return (ENOMEM); + return (0); } int Index: sys/kern/kern_proc.c =================================================================== --- sys/kern/kern_proc.c +++ sys/kern/kern_proc.c @@ -1526,50 +1526,20 @@ } static int -proc_read_mem(struct thread *td, struct proc *p, vm_offset_t offset, void* buf, - size_t len) -{ - struct iovec iov; - struct uio uio; - - iov.iov_base = (caddr_t)buf; - iov.iov_len = len; - uio.uio_iov = &iov; - uio.uio_iovcnt = 1; - uio.uio_offset = offset; - uio.uio_resid = (ssize_t)len; - uio.uio_segflg = UIO_SYSSPACE; - uio.uio_rw = UIO_READ; - uio.uio_td = td; - - return (proc_rwmem(p, &uio)); -} - -static int proc_read_string(struct thread *td, struct proc *p, const char *sptr, char *buf, size_t len) { - size_t i; - int error; + ssize_t n; - error = proc_read_mem(td, p, (vm_offset_t)sptr, buf, len); /* - * Reading the chunk may validly return EFAULT if the string is shorter - * than the chunk and is aligned at the end of the page, assuming the - * next page is not mapped. So if EFAULT is returned do a fallback to - * one byte read loop. + * This may return a short read if the string is shorter than the chunk + * and is aligned at the end of the page, and the following page is not + * mapped. */ - if (error == EFAULT) { - for (i = 0; i < len; i++, buf++, sptr++) { - error = proc_read_mem(td, p, (vm_offset_t)sptr, buf, 1); - if (error != 0) - return (error); - if (*buf == '\0') - break; - } - error = 0; - } - return (error); + n = proc_readmem(td, p, (vm_offset_t)sptr, buf, len); + if (n <= 0) + return (ENOMEM); + return (0); } #define PROC_AUXV_MAX 256 /* Safety limit on auxv size. */ @@ -1593,10 +1563,10 @@ size_t vsize, size; int i, error; - error = proc_read_mem(td, p, (vm_offset_t)(p->p_sysent->sv_psstrings), - &pss, sizeof(pss)); - if (error != 0) - return (error); + error = 0; + if (proc_readmem(td, p, (vm_offset_t)p->p_sysent->sv_psstrings, &pss, + sizeof(pss)) != sizeof(pss)) + return (ENOMEM); switch (type) { case PROC_ARG: vptr = (vm_offset_t)PTRIN(pss.ps_argvstr); @@ -1618,9 +1588,9 @@ if (vptr % 4 != 0) return (ENOEXEC); for (ptr = vptr, i = 0; i < PROC_AUXV_MAX; i++) { - error = proc_read_mem(td, p, ptr, &aux, sizeof(aux)); - if (error != 0) - return (error); + if (proc_readmem(td, p, ptr, &aux, sizeof(aux)) != + sizeof(aux)) + return (ENOMEM); if (aux.a_type == AT_NULL) break; ptr += sizeof(aux); @@ -1635,9 +1605,10 @@ return (EINVAL); } proc_vector32 = malloc(size, M_TEMP, M_WAITOK); - error = proc_read_mem(td, p, vptr, proc_vector32, size); - if (error != 0) + if (proc_readmem(td, p, vptr, proc_vector32, size) != size) { + error = ENOMEM; goto done; + } if (type == PROC_AUX) { *proc_vectorp = (char **)proc_vector32; *vsizep = vsize; @@ -1663,16 +1634,15 @@ vm_offset_t vptr, ptr; char **proc_vector; size_t vsize, size; - int error, i; + int i; #ifdef COMPAT_FREEBSD32 if (SV_PROC_FLAG(p, SV_ILP32) != 0) return (get_proc_vector32(td, p, proc_vectorp, vsizep, type)); #endif - error = proc_read_mem(td, p, (vm_offset_t)(p->p_sysent->sv_psstrings), - &pss, sizeof(pss)); - if (error != 0) - return (error); + if (proc_readmem(td, p, (vm_offset_t)p->p_sysent->sv_psstrings, &pss, + sizeof(pss)) != sizeof(pss)) + return (ENOMEM); switch (type) { case PROC_ARG: vptr = (vm_offset_t)pss.ps_argvstr; @@ -1709,9 +1679,9 @@ * to the allocated proc_vector. */ for (ptr = vptr, i = 0; i < PROC_AUXV_MAX; i++) { - error = proc_read_mem(td, p, ptr, &aux, sizeof(aux)); - if (error != 0) - return (error); + if (proc_readmem(td, p, ptr, &aux, sizeof(aux)) != + sizeof(aux)) + return (ENOMEM); if (aux.a_type == AT_NULL) break; ptr += sizeof(aux); @@ -1732,12 +1702,9 @@ return (EINVAL); /* In case we are built without INVARIANTS. */ } proc_vector = malloc(size, M_TEMP, M_WAITOK); - if (proc_vector == NULL) - return (ENOMEM); - error = proc_read_mem(td, p, vptr, proc_vector, size); - if (error != 0) { + if (proc_readmem(td, p, vptr, proc_vector, size) != size) { free(proc_vector, M_TEMP); - return (error); + return (ENOMEM); } *proc_vectorp = proc_vector; *vsizep = vsize; Index: sys/kern/sys_process.c =================================================================== --- sys/kern/sys_process.c +++ sys/kern/sys_process.c @@ -252,6 +252,7 @@ * from exiting out from under us until this operation completes. */ PROC_ASSERT_HELD(p); + PROC_LOCK_ASSERT(p, MA_NOTOWNED); /* * The map we want... @@ -328,6 +329,49 @@ } static int +proc_iop(struct thread *td, struct proc *p, vm_offset_t va, void *buf, + size_t len, enum uio_rw rw) +{ + struct iovec iov; + struct uio uio; + ssize_t slen; + int error; + + MPASS(len < SSIZE_MAX); + slen = (ssize_t)len; + + iov.iov_base = (caddr_t)buf; + iov.iov_len = len; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_offset = va; + uio.uio_resid = slen; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_rw = rw; + uio.uio_td = td; + error = proc_rwmem(p, &uio); + if (uio.uio_resid == slen) + return (-1); + return (slen - uio.uio_resid); +} + +int +proc_readmem(struct thread *td, struct proc *p, vm_offset_t va, void *buf, + size_t len) +{ + + return (proc_iop(td, p, va, buf, len, UIO_READ)); +} + +int +proc_writemem(struct thread *td, struct proc *p, vm_offset_t va, void *buf, + size_t len) +{ + + return (proc_iop(td, p, va, buf, len, UIO_WRITE)); +} + +static int ptrace_vm_entry(struct thread *td, struct proc *p, struct ptrace_vm_entry *pve) { struct vattr vattr; @@ -644,7 +688,7 @@ struct thread *td2 = NULL, *td3; struct ptrace_io_desc *piod = NULL; struct ptrace_lwpinfo *pl; - int error, write, tmp, num; + int error, num, tmp; int proctree_locked = 0; lwpid_t tid = 0, *buf; #ifdef COMPAT_FREEBSD32 @@ -674,7 +718,6 @@ break; } - write = 0; if (req == PT_TRACE_ME) { p = td->td_proc; PROC_LOCK(p); @@ -1033,46 +1076,28 @@ case PT_WRITE_I: case PT_WRITE_D: td2->td_dbgflags |= TDB_USERWR; - write = 1; - /* FALLTHROUGH */ + PROC_UNLOCK(p); + error = 0; + if (proc_writemem(td, p, (off_t)(uintptr_t)addr, &data, + sizeof(int)) != sizeof(int)) + error = ENOMEM; + else + CTR3(KTR_PTRACE, "PT_WRITE: pid %d: %p <= %#x", + p->p_pid, addr, data); + PROC_LOCK(p); + break; + case PT_READ_I: case PT_READ_D: PROC_UNLOCK(p); - tmp = 0; - /* write = 0 set above */ - iov.iov_base = write ? (caddr_t)&data : (caddr_t)&tmp; - iov.iov_len = sizeof(int); - uio.uio_iov = &iov; - uio.uio_iovcnt = 1; - uio.uio_offset = (off_t)(uintptr_t)addr; - uio.uio_resid = sizeof(int); - uio.uio_segflg = UIO_SYSSPACE; /* i.e.: the uap */ - uio.uio_rw = write ? UIO_WRITE : UIO_READ; - uio.uio_td = td; - error = proc_rwmem(p, &uio); - if (uio.uio_resid != 0) { - /* - * XXX proc_rwmem() doesn't currently return ENOSPC, - * so I think write() can bogusly return 0. - * XXX what happens for short writes? We don't want - * to write partial data. - * XXX proc_rwmem() returns EPERM for other invalid - * addresses. Convert this to EINVAL. Does this - * clobber returns of EPERM for other reasons? - */ - if (error == 0 || error == ENOSPC || error == EPERM) - error = EINVAL; /* EOF */ - } - if (!write) - td->td_retval[0] = tmp; - if (error == 0) { - if (write) - CTR3(KTR_PTRACE, "PT_WRITE: pid %d: %p <= %#x", - p->p_pid, addr, data); - else - CTR3(KTR_PTRACE, "PT_READ: pid %d: %p >= %#x", - p->p_pid, addr, tmp); - } + error = tmp = 0; + if (proc_readmem(td, p, (off_t)(uintptr_t)addr, &tmp, + sizeof(int)) != sizeof(int)) + error = ENOMEM; + else + CTR3(KTR_PTRACE, "PT_READ: pid %d: %p >= %#x", + p->p_pid, addr, tmp); + td->td_retval[0] = tmp; PROC_LOCK(p); break; Index: sys/mips/mips/pm_machdep.c =================================================================== --- sys/mips/mips/pm_machdep.c +++ sys/mips/mips/pm_machdep.c @@ -214,39 +214,19 @@ static int ptrace_read_int(struct thread *td, off_t addr, int *v) { - struct iovec iov; - struct uio uio; - - PROC_LOCK_ASSERT(td->td_proc, MA_NOTOWNED); - iov.iov_base = (caddr_t) v; - iov.iov_len = sizeof(int); - uio.uio_iov = &iov; - uio.uio_iovcnt = 1; - uio.uio_offset = (off_t)addr; - uio.uio_resid = sizeof(int); - uio.uio_segflg = UIO_SYSSPACE; - uio.uio_rw = UIO_READ; - uio.uio_td = td; - return proc_rwmem(td->td_proc, &uio); + + if (proc_readmem(td, td->proc, addr, v, sizeof(*v)) != sizeof(*v)) + return (ENOMEM); + return (0); } static int ptrace_write_int(struct thread *td, off_t addr, int v) { - struct iovec iov; - struct uio uio; - - PROC_LOCK_ASSERT(td->td_proc, MA_NOTOWNED); - iov.iov_base = (caddr_t) &v; - iov.iov_len = sizeof(int); - uio.uio_iov = &iov; - uio.uio_iovcnt = 1; - uio.uio_offset = (off_t)addr; - uio.uio_resid = sizeof(int); - uio.uio_segflg = UIO_SYSSPACE; - uio.uio_rw = UIO_WRITE; - uio.uio_td = td; - return proc_rwmem(td->td_proc, &uio); + + if (proc_writemem(td, td->proc, addr, &v, sizeof(v)) != sizeof(v)) + return (ENOMEM); + return (0); } int Index: sys/sys/ptrace.h =================================================================== --- sys/sys/ptrace.h +++ sys/sys/ptrace.h @@ -166,6 +166,10 @@ int proc_write_dbregs(struct thread *_td, struct dbreg *_dbreg); int proc_sstep(struct thread *_td); int proc_rwmem(struct proc *_p, struct uio *_uio); +int proc_readmem(struct thread *_td, struct proc *_p, vm_offset_t _va, + void *_buf, size_t _len); +int proc_writemem(struct thread *_td, struct proc *_p, vm_offset_t _va, + void *_buf, size_t _len); #ifdef COMPAT_FREEBSD32 struct reg32; struct fpreg32;