This adds support for PTRACE_GETSIGINFO and PTRACE_SETSIGINFO in
compat_ptrace_request. It relies on existing arch definitions for
copy_siginfo_to_user32 and copy_siginfo_from_user32.
On powerpc, this fixes a longstanding regression of 32-bit ptrace
calls on 64-bit kernels vs native calls (64-bit calls or 32-bit
kernels). This can be seen in a 32-bit call using PTRACE_GETSIGINFO
to examine e.g. siginfo_t.si_addr from a signal that sets it.
(This was broken as of 2.6.24 and, I presume, many or all prior versions.)
Signed-off-by: Roland McGrath <roland@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
return (data & ~PTRACE_O_MASK) ? -EINVAL : 0;
}
return (data & ~PTRACE_O_MASK) ? -EINVAL : 0;
}
-static int ptrace_getsiginfo(struct task_struct *child, siginfo_t __user * data)
+static int ptrace_getsiginfo(struct task_struct *child, siginfo_t *info)
int error = -ESRCH;
read_lock(&tasklist_lock);
int error = -ESRCH;
read_lock(&tasklist_lock);
error = -EINVAL;
spin_lock_irq(&child->sighand->siglock);
if (likely(child->last_siginfo != NULL)) {
error = -EINVAL;
spin_lock_irq(&child->sighand->siglock);
if (likely(child->last_siginfo != NULL)) {
- lastinfo = *child->last_siginfo;
+ *info = *child->last_siginfo;
error = 0;
}
spin_unlock_irq(&child->sighand->siglock);
}
read_unlock(&tasklist_lock);
error = 0;
}
spin_unlock_irq(&child->sighand->siglock);
}
read_unlock(&tasklist_lock);
- if (!error)
- return copy_siginfo_to_user(data, &lastinfo);
-static int ptrace_setsiginfo(struct task_struct *child, siginfo_t __user * data)
+static int ptrace_setsiginfo(struct task_struct *child, const siginfo_t *info)
- if (copy_from_user(&newinfo, data, sizeof (siginfo_t)))
- return -EFAULT;
-
read_lock(&tasklist_lock);
if (likely(child->sighand != NULL)) {
error = -EINVAL;
spin_lock_irq(&child->sighand->siglock);
if (likely(child->last_siginfo != NULL)) {
read_lock(&tasklist_lock);
if (likely(child->sighand != NULL)) {
error = -EINVAL;
spin_lock_irq(&child->sighand->siglock);
if (likely(child->last_siginfo != NULL)) {
- *child->last_siginfo = newinfo;
+ *child->last_siginfo = *info;
error = 0;
}
spin_unlock_irq(&child->sighand->siglock);
error = 0;
}
spin_unlock_irq(&child->sighand->siglock);
long addr, long data)
{
int ret = -EIO;
long addr, long data)
{
int ret = -EIO;
switch (request) {
case PTRACE_PEEKTEXT:
switch (request) {
case PTRACE_PEEKTEXT:
case PTRACE_GETEVENTMSG:
ret = put_user(child->ptrace_message, (unsigned long __user *) data);
break;
case PTRACE_GETEVENTMSG:
ret = put_user(child->ptrace_message, (unsigned long __user *) data);
break;
- ret = ptrace_getsiginfo(child, (siginfo_t __user *) data);
+ ret = ptrace_getsiginfo(child, &siginfo);
+ if (!ret)
+ ret = copy_siginfo_to_user((siginfo_t __user *) data,
+ &siginfo);
- ret = ptrace_setsiginfo(child, (siginfo_t __user *) data);
+ if (copy_from_user(&siginfo, (siginfo_t __user *) data,
+ sizeof siginfo))
+ ret = -EFAULT;
+ else
+ ret = ptrace_setsiginfo(child, &siginfo);
case PTRACE_DETACH: /* detach a process that was attached. */
ret = ptrace_detach(child, data);
break;
case PTRACE_DETACH: /* detach a process that was attached. */
ret = ptrace_detach(child, data);
break;
{
compat_ulong_t __user *datap = compat_ptr(data);
compat_ulong_t word;
{
compat_ulong_t __user *datap = compat_ptr(data);
compat_ulong_t word;
int ret;
switch (request) {
int ret;
switch (request) {
ret = put_user((compat_ulong_t) child->ptrace_message, datap);
break;
ret = put_user((compat_ulong_t) child->ptrace_message, datap);
break;
+ case PTRACE_GETSIGINFO:
+ ret = ptrace_getsiginfo(child, &siginfo);
+ if (!ret)
+ ret = copy_siginfo_to_user32(
+ (struct compat_siginfo __user *) datap,
+ &siginfo);
+ break;
+
+ case PTRACE_SETSIGINFO:
+ memset(&siginfo, 0, sizeof siginfo);
+ if (copy_siginfo_from_user32(
+ &siginfo, (struct compat_siginfo __user *) datap))
+ ret = -EFAULT;
+ else
+ ret = ptrace_setsiginfo(child, &siginfo);
+ break;
+
default:
ret = ptrace_request(child, request, addr, data);
}
default:
ret = ptrace_request(child, request, addr, data);
}