diff options
author | Roland McGrath <roland@redhat.com> | 2008-04-20 16:10:12 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-21 18:53:41 -0400 |
commit | e16b27816462de700f9508d86954410c41105dc2 (patch) | |
tree | f3827a23a43dcaad06362d511ac20d576b9128d5 /kernel/ptrace.c | |
parent | 553a56726be86c09cfa53c84da1ea0e2043e364e (diff) |
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>
Diffstat (limited to 'kernel/ptrace.c')
-rw-r--r-- | kernel/ptrace.c | 48 |
1 files changed, 35 insertions, 13 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index fdb34e86f923..67e392ed5496 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
@@ -323,9 +323,8 @@ static int ptrace_setoptions(struct task_struct *child, long data) | |||
323 | return (data & ~PTRACE_O_MASK) ? -EINVAL : 0; | 323 | return (data & ~PTRACE_O_MASK) ? -EINVAL : 0; |
324 | } | 324 | } |
325 | 325 | ||
326 | static int ptrace_getsiginfo(struct task_struct *child, siginfo_t __user * data) | 326 | static int ptrace_getsiginfo(struct task_struct *child, siginfo_t *info) |
327 | { | 327 | { |
328 | siginfo_t lastinfo; | ||
329 | int error = -ESRCH; | 328 | int error = -ESRCH; |
330 | 329 | ||
331 | read_lock(&tasklist_lock); | 330 | read_lock(&tasklist_lock); |
@@ -333,31 +332,25 @@ static int ptrace_getsiginfo(struct task_struct *child, siginfo_t __user * data) | |||
333 | error = -EINVAL; | 332 | error = -EINVAL; |
334 | spin_lock_irq(&child->sighand->siglock); | 333 | spin_lock_irq(&child->sighand->siglock); |
335 | if (likely(child->last_siginfo != NULL)) { | 334 | if (likely(child->last_siginfo != NULL)) { |
336 | lastinfo = *child->last_siginfo; | 335 | *info = *child->last_siginfo; |
337 | error = 0; | 336 | error = 0; |
338 | } | 337 | } |
339 | spin_unlock_irq(&child->sighand->siglock); | 338 | spin_unlock_irq(&child->sighand->siglock); |
340 | } | 339 | } |
341 | read_unlock(&tasklist_lock); | 340 | read_unlock(&tasklist_lock); |
342 | if (!error) | ||
343 | return copy_siginfo_to_user(data, &lastinfo); | ||
344 | return error; | 341 | return error; |
345 | } | 342 | } |
346 | 343 | ||
347 | static int ptrace_setsiginfo(struct task_struct *child, siginfo_t __user * data) | 344 | static int ptrace_setsiginfo(struct task_struct *child, const siginfo_t *info) |
348 | { | 345 | { |
349 | siginfo_t newinfo; | ||
350 | int error = -ESRCH; | 346 | int error = -ESRCH; |
351 | 347 | ||
352 | if (copy_from_user(&newinfo, data, sizeof (siginfo_t))) | ||
353 | return -EFAULT; | ||
354 | |||
355 | read_lock(&tasklist_lock); | 348 | read_lock(&tasklist_lock); |
356 | if (likely(child->sighand != NULL)) { | 349 | if (likely(child->sighand != NULL)) { |
357 | error = -EINVAL; | 350 | error = -EINVAL; |
358 | spin_lock_irq(&child->sighand->siglock); | 351 | spin_lock_irq(&child->sighand->siglock); |
359 | if (likely(child->last_siginfo != NULL)) { | 352 | if (likely(child->last_siginfo != NULL)) { |
360 | *child->last_siginfo = newinfo; | 353 | *child->last_siginfo = *info; |
361 | error = 0; | 354 | error = 0; |
362 | } | 355 | } |
363 | spin_unlock_irq(&child->sighand->siglock); | 356 | spin_unlock_irq(&child->sighand->siglock); |
@@ -424,6 +417,7 @@ int ptrace_request(struct task_struct *child, long request, | |||
424 | long addr, long data) | 417 | long addr, long data) |
425 | { | 418 | { |
426 | int ret = -EIO; | 419 | int ret = -EIO; |
420 | siginfo_t siginfo; | ||
427 | 421 | ||
428 | switch (request) { | 422 | switch (request) { |
429 | case PTRACE_PEEKTEXT: | 423 | case PTRACE_PEEKTEXT: |
@@ -442,12 +436,22 @@ int ptrace_request(struct task_struct *child, long request, | |||
442 | case PTRACE_GETEVENTMSG: | 436 | case PTRACE_GETEVENTMSG: |
443 | ret = put_user(child->ptrace_message, (unsigned long __user *) data); | 437 | ret = put_user(child->ptrace_message, (unsigned long __user *) data); |
444 | break; | 438 | break; |
439 | |||
445 | case PTRACE_GETSIGINFO: | 440 | case PTRACE_GETSIGINFO: |
446 | ret = ptrace_getsiginfo(child, (siginfo_t __user *) data); | 441 | ret = ptrace_getsiginfo(child, &siginfo); |
442 | if (!ret) | ||
443 | ret = copy_siginfo_to_user((siginfo_t __user *) data, | ||
444 | &siginfo); | ||
447 | break; | 445 | break; |
446 | |||
448 | case PTRACE_SETSIGINFO: | 447 | case PTRACE_SETSIGINFO: |
449 | ret = ptrace_setsiginfo(child, (siginfo_t __user *) data); | 448 | if (copy_from_user(&siginfo, (siginfo_t __user *) data, |
449 | sizeof siginfo)) | ||
450 | ret = -EFAULT; | ||
451 | else | ||
452 | ret = ptrace_setsiginfo(child, &siginfo); | ||
450 | break; | 453 | break; |
454 | |||
451 | case PTRACE_DETACH: /* detach a process that was attached. */ | 455 | case PTRACE_DETACH: /* detach a process that was attached. */ |
452 | ret = ptrace_detach(child, data); | 456 | ret = ptrace_detach(child, data); |
453 | break; | 457 | break; |
@@ -616,6 +620,7 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request, | |||
616 | { | 620 | { |
617 | compat_ulong_t __user *datap = compat_ptr(data); | 621 | compat_ulong_t __user *datap = compat_ptr(data); |
618 | compat_ulong_t word; | 622 | compat_ulong_t word; |
623 | siginfo_t siginfo; | ||
619 | int ret; | 624 | int ret; |
620 | 625 | ||
621 | switch (request) { | 626 | switch (request) { |
@@ -638,6 +643,23 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request, | |||
638 | ret = put_user((compat_ulong_t) child->ptrace_message, datap); | 643 | ret = put_user((compat_ulong_t) child->ptrace_message, datap); |
639 | break; | 644 | break; |
640 | 645 | ||
646 | case PTRACE_GETSIGINFO: | ||
647 | ret = ptrace_getsiginfo(child, &siginfo); | ||
648 | if (!ret) | ||
649 | ret = copy_siginfo_to_user32( | ||
650 | (struct compat_siginfo __user *) datap, | ||
651 | &siginfo); | ||
652 | break; | ||
653 | |||
654 | case PTRACE_SETSIGINFO: | ||
655 | memset(&siginfo, 0, sizeof siginfo); | ||
656 | if (copy_siginfo_from_user32( | ||
657 | &siginfo, (struct compat_siginfo __user *) datap)) | ||
658 | ret = -EFAULT; | ||
659 | else | ||
660 | ret = ptrace_setsiginfo(child, &siginfo); | ||
661 | break; | ||
662 | |||
641 | default: | 663 | default: |
642 | ret = ptrace_request(child, request, addr, data); | 664 | ret = ptrace_request(child, request, addr, data); |
643 | } | 665 | } |