Hello everyone, While playing a bit with ptrace to do some debugging I stumbled upon something that looks like a bug. While trying to write to the ptrace'd process using PT_IO in combinaison with PIOD_WRITE_D I kept getting EFAULTs. PIOD_READ_D would work fine on the same address though, and even more weirdly PIOD_WRITE_I would also work fine on the same address. Even more strange, PT_WRITE_D works fine too on the same address. So in effect, only PT_IO + PIOD_WRITE_D would EFAULT on this address.
If this is the expected behavior (I really doubt it), then the man page is wrong because it states clearly that *_I and *_D are equivalent. Digging a bit on the implementation I traced it back to rev 1.33 of sys_process.c. The old implementation used procfs_domem to do the uvm_io call, and based the decision as to use UVM_IO_FIXPROT or not on the uio.uio_rw field (UIO_WRITE meant FIXPROT vs UIO_READ). However the new implementation, in process_domem takes a third parameter, req, the ptrace request and would use UVM_IO_FIXPROT only in the PT_WRITE_I case (rings any bell?). That's why PT_WRITE_D will EFAULT in any case. Oh and PT_WRITE_I and PT_WRITE_D work because they both use PT_WRITE_I as the req parameter : process_domem(p, t, &uio, write ? PT_WRITE_I : .... So I came up with the following diff (kind of the big hammer approach), which gets rid of the req parameters and base the UVM_IO_FIXPROT decision on the uio.uio_rw field as the previous code (10 years ago!) was doing. nota: I was curious as to how gdb worked with this, but turns out they just bruteforce both PIOD_WRITE_I and PIOD_WRITE_D flag until it works :). diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c index 60ec50e..4d589e7 100644 --- a/sys/kern/sys_process.c +++ b/sys/kern/sys_process.c @@ -368,8 +368,7 @@ sys_ptrace(struct proc *p, void *v, register_t *retval) uio.uio_segflg = UIO_SYSSPACE; uio.uio_rw = write ? UIO_WRITE : UIO_READ; uio.uio_procp = p; - error = process_domem(p, t, &uio, write ? PT_WRITE_I : - PT_READ_I); + error = process_domem(p, t, &uio); if (write == 0) *retval = temp; return (error); @@ -387,23 +386,14 @@ sys_ptrace(struct proc *p, void *v, register_t *retval) uio.uio_procp = p; switch (piod.piod_op) { case PIOD_READ_I: - req = PT_READ_I; - uio.uio_rw = UIO_READ; - break; case PIOD_READ_D: - req = PT_READ_D; uio.uio_rw = UIO_READ; break; case PIOD_WRITE_I: - req = PT_WRITE_I; - uio.uio_rw = UIO_WRITE; - break; case PIOD_WRITE_D: - req = PT_WRITE_D; uio.uio_rw = UIO_WRITE; break; case PIOD_READ_AUXV: - req = PT_READ_D; uio.uio_rw = UIO_READ; temp = tr->ps_emul->e_arglen * sizeof(char *); if (uio.uio_offset > temp) @@ -418,7 +408,7 @@ sys_ptrace(struct proc *p, void *v, register_t *retval) default: return (EINVAL); } - error = process_domem(p, t, &uio, req); + error = process_domem(p, t, &uio); piod.piod_len -= uio.uio_resid; (void) copyout(&piod, SCARG(uap, addr), sizeof(piod)); return (error); @@ -711,7 +701,7 @@ process_checkioperm(struct proc *p, struct process *tr) } int -process_domem(struct proc *curp, struct proc *p, struct uio *uio, int req) +process_domem(struct proc *curp, struct proc *p, struct uio *uio) { struct vmspace *vm; int error; @@ -734,11 +724,11 @@ process_domem(struct proc *curp, struct proc *p, struct uio *uio, int req) vm->vm_refcnt++; error = uvm_io(&vm->vm_map, uio, - (req == PT_WRITE_I) ? UVM_IO_FIXPROT : 0); + (uio->uio_rw == UIO_WRITE) ? UVM_IO_FIXPROT : 0); uvmspace_free(vm); - if (error == 0 && req == PT_WRITE_I) + if (error == 0 && uio->uio_rw == UIO_WRITE) pmap_proc_iflush(p, addr, len); return (error); diff --git a/sys/sys/ptrace.h b/sys/sys/ptrace.h index 3c8fda3..b8b76a0 100644 --- a/sys/sys/ptrace.h +++ b/sys/sys/ptrace.h @@ -116,7 +116,7 @@ int process_write_fpregs(struct proc *p, struct fpreg *regs); #endif int process_write_regs(struct proc *p, struct reg *regs); int process_checkioperm(struct proc *, struct process *); -int process_domem(struct proc *, struct proc *, struct uio *, int); +int process_domem(struct proc *, struct proc *, struct uio *); #ifndef FIX_SSTEP #define FIX_SSTEP(p)