Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 778959db authored by Martin Schwidefsky's avatar Martin Schwidefsky Committed by Linus Torvalds
Browse files

[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: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 854715be
Loading
Loading
Loading
Loading
+43 −5
Original line number Diff line number Diff line
@@ -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;