ptrace: compat_ptrace_request siginfo
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>
This commit is contained in:
Родитель
553a56726b
Коммит
e16b278164
|
@ -323,9 +323,8 @@ static int ptrace_setoptions(struct task_struct *child, long data)
|
||||||
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)
|
||||||
{
|
{
|
||||||
siginfo_t lastinfo;
|
|
||||||
int error = -ESRCH;
|
int error = -ESRCH;
|
||||||
|
|
||||||
read_lock(&tasklist_lock);
|
read_lock(&tasklist_lock);
|
||||||
|
@ -333,31 +332,25 @@ static int ptrace_getsiginfo(struct task_struct *child, siginfo_t __user * data)
|
||||||
error = -EINVAL;
|
error = -EINVAL;
|
||||||
spin_lock_irq(&child->sighand->siglock);
|
spin_lock_irq(&child->sighand->siglock);
|
||||||
if (likely(child->last_siginfo != NULL)) {
|
if (likely(child->last_siginfo != NULL)) {
|
||||||
lastinfo = *child->last_siginfo;
|
*info = *child->last_siginfo;
|
||||||
error = 0;
|
error = 0;
|
||||||
}
|
}
|
||||||
spin_unlock_irq(&child->sighand->siglock);
|
spin_unlock_irq(&child->sighand->siglock);
|
||||||
}
|
}
|
||||||
read_unlock(&tasklist_lock);
|
read_unlock(&tasklist_lock);
|
||||||
if (!error)
|
|
||||||
return copy_siginfo_to_user(data, &lastinfo);
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ptrace_setsiginfo(struct task_struct *child, siginfo_t __user * data)
|
static int ptrace_setsiginfo(struct task_struct *child, const siginfo_t *info)
|
||||||
{
|
{
|
||||||
siginfo_t newinfo;
|
|
||||||
int error = -ESRCH;
|
int error = -ESRCH;
|
||||||
|
|
||||||
if (copy_from_user(&newinfo, data, sizeof (siginfo_t)))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
read_lock(&tasklist_lock);
|
read_lock(&tasklist_lock);
|
||||||
if (likely(child->sighand != NULL)) {
|
if (likely(child->sighand != NULL)) {
|
||||||
error = -EINVAL;
|
error = -EINVAL;
|
||||||
spin_lock_irq(&child->sighand->siglock);
|
spin_lock_irq(&child->sighand->siglock);
|
||||||
if (likely(child->last_siginfo != NULL)) {
|
if (likely(child->last_siginfo != NULL)) {
|
||||||
*child->last_siginfo = newinfo;
|
*child->last_siginfo = *info;
|
||||||
error = 0;
|
error = 0;
|
||||||
}
|
}
|
||||||
spin_unlock_irq(&child->sighand->siglock);
|
spin_unlock_irq(&child->sighand->siglock);
|
||||||
|
@ -424,6 +417,7 @@ int ptrace_request(struct task_struct *child, long request,
|
||||||
long addr, long data)
|
long addr, long data)
|
||||||
{
|
{
|
||||||
int ret = -EIO;
|
int ret = -EIO;
|
||||||
|
siginfo_t siginfo;
|
||||||
|
|
||||||
switch (request) {
|
switch (request) {
|
||||||
case PTRACE_PEEKTEXT:
|
case PTRACE_PEEKTEXT:
|
||||||
|
@ -442,12 +436,22 @@ int ptrace_request(struct task_struct *child, long request,
|
||||||
case PTRACE_GETEVENTMSG:
|
case PTRACE_GETEVENTMSG:
|
||||||
ret = put_user(child->ptrace_message, (unsigned long __user *) data);
|
ret = put_user(child->ptrace_message, (unsigned long __user *) data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PTRACE_GETSIGINFO:
|
case PTRACE_GETSIGINFO:
|
||||||
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);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PTRACE_SETSIGINFO:
|
case PTRACE_SETSIGINFO:
|
||||||
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);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PTRACE_DETACH: /* detach a process that was attached. */
|
case PTRACE_DETACH: /* detach a process that was attached. */
|
||||||
ret = ptrace_detach(child, data);
|
ret = ptrace_detach(child, data);
|
||||||
break;
|
break;
|
||||||
|
@ -616,6 +620,7 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request,
|
||||||
{
|
{
|
||||||
compat_ulong_t __user *datap = compat_ptr(data);
|
compat_ulong_t __user *datap = compat_ptr(data);
|
||||||
compat_ulong_t word;
|
compat_ulong_t word;
|
||||||
|
siginfo_t siginfo;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
switch (request) {
|
switch (request) {
|
||||||
|
@ -638,6 +643,23 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request,
|
||||||
ret = put_user((compat_ulong_t) child->ptrace_message, datap);
|
ret = put_user((compat_ulong_t) child->ptrace_message, datap);
|
||||||
break;
|
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:
|
default:
|
||||||
ret = ptrace_request(child, request, addr, data);
|
ret = ptrace_request(child, request, addr, data);
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче