[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
|
@ -40,6 +40,7 @@
|
||||||
#include <asm/pgalloc.h>
|
#include <asm/pgalloc.h>
|
||||||
#include <asm/system.h>
|
#include <asm/system.h>
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
|
#include <asm/unistd.h>
|
||||||
|
|
||||||
#ifdef CONFIG_S390_SUPPORT
|
#ifdef CONFIG_S390_SUPPORT
|
||||||
#include "compat_ptrace.h"
|
#include "compat_ptrace.h"
|
||||||
|
@ -130,13 +131,19 @@ static int
|
||||||
peek_user(struct task_struct *child, addr_t addr, addr_t data)
|
peek_user(struct task_struct *child, addr_t addr, addr_t data)
|
||||||
{
|
{
|
||||||
struct user *dummy = NULL;
|
struct user *dummy = NULL;
|
||||||
addr_t offset, tmp;
|
addr_t offset, tmp, mask;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Stupid gdb peeks/pokes the access registers in 64 bit with
|
* Stupid gdb peeks/pokes the access registers in 64 bit with
|
||||||
* an alignment of 4. Programmers from hell...
|
* 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;
|
return -EIO;
|
||||||
|
|
||||||
if (addr < (addr_t) &dummy->regs.acrs) {
|
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
|
* access registers are stored in the thread structure
|
||||||
*/
|
*/
|
||||||
offset = addr - (addr_t) &dummy->regs.acrs;
|
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);
|
tmp = *(addr_t *)((addr_t) &child->thread.acrs + offset);
|
||||||
|
|
||||||
} else if (addr == (addr_t) &dummy->regs.orig_gpr2) {
|
} 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;
|
offset = addr - (addr_t) &dummy->regs.fp_regs;
|
||||||
tmp = *(addr_t *)((addr_t) &child->thread.fp_regs + offset);
|
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)) {
|
} 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)
|
poke_user(struct task_struct *child, addr_t addr, addr_t data)
|
||||||
{
|
{
|
||||||
struct user *dummy = NULL;
|
struct user *dummy = NULL;
|
||||||
addr_t offset;
|
addr_t offset, mask;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Stupid gdb peeks/pokes the access registers in 64 bit with
|
* Stupid gdb peeks/pokes the access registers in 64 bit with
|
||||||
* an alignment of 4. Programmers from hell indeed...
|
* 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;
|
return -EIO;
|
||||||
|
|
||||||
if (addr < (addr_t) &dummy->regs.acrs) {
|
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
|
* access registers are stored in the thread structure
|
||||||
*/
|
*/
|
||||||
offset = addr - (addr_t) &dummy->regs.acrs;
|
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;
|
*(addr_t *)((addr_t) &child->thread.acrs + offset) = data;
|
||||||
|
|
||||||
} else if (addr == (addr_t) &dummy->regs.orig_gpr2) {
|
} 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
|
* floating point regs. are stored in the thread structure
|
||||||
*/
|
*/
|
||||||
if (addr == (addr_t) &dummy->regs.fp_regs.fpc &&
|
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;
|
return -EINVAL;
|
||||||
offset = addr - (addr_t) &dummy->regs.fp_regs;
|
offset = addr - (addr_t) &dummy->regs.fp_regs;
|
||||||
*(addr_t *)((addr_t) &child->thread.fp_regs + offset) = data;
|
*(addr_t *)((addr_t) &child->thread.fp_regs + offset) = data;
|
||||||
|
|
Loading…
Reference in New Issue