PTRACE_PEEKUSER is documented to read a word from the user area, whose partial layout on x86_64 is:
struct user
{
struct user_regs_struct regs;
int u_fpvalid;
struct user_fpregs_struct i387;
//...
int u_debugreg [8];
};
While one can happily call PTRACE_PEEKUSER with an offset which lies within regs or u_debugreg, passing an offset which is within i387 returns EIO unconditionally.
Looking at the code in the kernel, I can see that reading inside regs and u_debugreg are the only supported offsets:
case PTRACE_PEEKUSR: {
unsigned long tmp;
ret = -EIO;
if ((addr & (sizeof(data) - 1)) || addr >= sizeof(struct user))
break;
tmp = 0; /* Default return condition */
if (addr < sizeof(struct user_regs_struct))
tmp = getreg(child, addr);
else if (addr >= offsetof(struct user, u_debugreg[0]) &&
addr <= offsetof(struct user, u_debugreg[7])) {
addr -= offsetof(struct user, u_debugreg[0]);
tmp = ptrace_get_debugreg(child, addr / sizeof(data));
}
ret = put_user(tmp, datap);
break;
}
I know that reading inside i387 is not supported. I know that I can read those registers by calling PTRACE_GETFPREGS instead. My question is: is there a specific reason why reading those registers using PTRACE_PEEKUSER and writing them with PTRACE_POKEUSER is disabled?
My guess is that it's because most of the fields in user_fpregs_struct are smaller than 64 bits, therefore reads and writes may cover multiple registers. However, I'm writing a resource which will be public and will have a note on this, so I'd rather be certain that my guess is right.