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)