[PATCH] s390: ptrace peek and poke
The special cases of peek and poke on acrs[15] and the fpc register are not handled correctly. A poke on acrs[15] will clobber the 4 bytes after the access registers in the thread_info structure. That happens to be the kernel stack pointer. A poke on the fpc with an invalid value is not caught by the validity check. On the next context switch the broken fpc value will cause a program check in the kernel. Improving the checks in peek and poke fixes this. Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
854715be73
commit
778959db97
1 changed files with 43 additions and 5 deletions
|
@ -40,6 +40,7 @@
|
|||
#include <asm/pgalloc.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/unistd.h>
|
||||
|
||||
#ifdef CONFIG_S390_SUPPORT
|
||||
#include "compat_ptrace.h"
|
||||
|
@ -130,13 +131,19 @@ static int
|
|||
peek_user(struct task_struct *child, addr_t addr, addr_t data)
|
||||
{
|
||||
struct user *dummy = NULL;
|
||||
addr_t offset, tmp;
|
||||
addr_t offset, tmp, mask;
|
||||
|
||||
/*
|
||||
* Stupid gdb peeks/pokes the access registers in 64 bit with
|
||||
* an alignment of 4. Programmers from hell...
|
||||
*/
|
||||
if ((addr & 3) || addr > sizeof(struct user) - __ADDR_MASK)
|
||||
mask = __ADDR_MASK;
|
||||
#ifdef CONFIG_ARCH_S390X
|
||||
if (addr >= (addr_t) &dummy->regs.acrs &&
|
||||
addr < (addr_t) &dummy->regs.orig_gpr2)
|
||||
mask = 3;
|
||||
#endif
|
||||
if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK)
|
||||
return -EIO;
|
||||
|
||||
if (addr < (addr_t) &dummy->regs.acrs) {
|
||||
|
@ -153,6 +160,16 @@ peek_user(struct task_struct *child, addr_t addr, addr_t data)
|
|||
* access registers are stored in the thread structure
|
||||
*/
|
||||
offset = addr - (addr_t) &dummy->regs.acrs;
|
||||
#ifdef CONFIG_ARCH_S390X
|
||||
/*
|
||||
* Very special case: old & broken 64 bit gdb reading
|
||||
* from acrs[15]. Result is a 64 bit value. Read the
|
||||
* 32 bit acrs[15] value and shift it by 32. Sick...
|
||||
*/
|
||||
if (addr == (addr_t) &dummy->regs.acrs[15])
|
||||
tmp = ((unsigned long) child->thread.acrs[15]) << 32;
|
||||
else
|
||||
#endif
|
||||
tmp = *(addr_t *)((addr_t) &child->thread.acrs + offset);
|
||||
|
||||
} else if (addr == (addr_t) &dummy->regs.orig_gpr2) {
|
||||
|
@ -167,6 +184,9 @@ peek_user(struct task_struct *child, addr_t addr, addr_t data)
|
|||
*/
|
||||
offset = addr - (addr_t) &dummy->regs.fp_regs;
|
||||
tmp = *(addr_t *)((addr_t) &child->thread.fp_regs + offset);
|
||||
if (addr == (addr_t) &dummy->regs.fp_regs.fpc)
|
||||
tmp &= (unsigned long) FPC_VALID_MASK
|
||||
<< (BITS_PER_LONG - 32);
|
||||
|
||||
} else if (addr < (addr_t) (&dummy->regs.per_info + 1)) {
|
||||
/*
|
||||
|
@ -191,13 +211,19 @@ static int
|
|||
poke_user(struct task_struct *child, addr_t addr, addr_t data)
|
||||
{
|
||||
struct user *dummy = NULL;
|
||||
addr_t offset;
|
||||
addr_t offset, mask;
|
||||
|
||||
/*
|
||||
* Stupid gdb peeks/pokes the access registers in 64 bit with
|
||||
* an alignment of 4. Programmers from hell indeed...
|
||||
*/
|
||||
if ((addr & 3) || addr > sizeof(struct user) - __ADDR_MASK)
|
||||
mask = __ADDR_MASK;
|
||||
#ifdef CONFIG_ARCH_S390X
|
||||
if (addr >= (addr_t) &dummy->regs.acrs &&
|
||||
addr < (addr_t) &dummy->regs.orig_gpr2)
|
||||
mask = 3;
|
||||
#endif
|
||||
if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK)
|
||||
return -EIO;
|
||||
|
||||
if (addr < (addr_t) &dummy->regs.acrs) {
|
||||
|
@ -224,6 +250,17 @@ poke_user(struct task_struct *child, addr_t addr, addr_t data)
|
|||
* access registers are stored in the thread structure
|
||||
*/
|
||||
offset = addr - (addr_t) &dummy->regs.acrs;
|
||||
#ifdef CONFIG_ARCH_S390X
|
||||
/*
|
||||
* Very special case: old & broken 64 bit gdb writing
|
||||
* to acrs[15] with a 64 bit value. Ignore the lower
|
||||
* half of the value and write the upper 32 bit to
|
||||
* acrs[15]. Sick...
|
||||
*/
|
||||
if (addr == (addr_t) &dummy->regs.acrs[15])
|
||||
child->thread.acrs[15] = (unsigned int) (data >> 32);
|
||||
else
|
||||
#endif
|
||||
*(addr_t *)((addr_t) &child->thread.acrs + offset) = data;
|
||||
|
||||
} else if (addr == (addr_t) &dummy->regs.orig_gpr2) {
|
||||
|
@ -237,7 +274,8 @@ poke_user(struct task_struct *child, addr_t addr, addr_t data)
|
|||
* floating point regs. are stored in the thread structure
|
||||
*/
|
||||
if (addr == (addr_t) &dummy->regs.fp_regs.fpc &&
|
||||
(data & ~FPC_VALID_MASK) != 0)
|
||||
(data & ~((unsigned long) FPC_VALID_MASK
|
||||
<< (BITS_PER_LONG - 32))) != 0)
|
||||
return -EINVAL;
|
||||
offset = addr - (addr_t) &dummy->regs.fp_regs;
|
||||
*(addr_t *)((addr_t) &child->thread.fp_regs + offset) = data;
|
||||
|
|
Loading…
Reference in a new issue