Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > fc11cd6e1c513a17304da94a5390f3cd > files > 3812

kernel-2.6.18-194.11.1.el5.src.rpm

From: Roland McGrath <roland@redhat.com>
Date: Wed, 7 Nov 2007 16:20:21 -0800
Subject: [utrace] s390 regs fixes
Message-id: 20071108002021.C4B224D0564@magilla.localdomain
O-Subject: [RHEL5.2 PATCH] RHBZ#325451: utrace s390 regs
Bugzilla: 325451

This fixes some bugs (regressions from RHEL4 and upstream) on s390x for
31-bit processes using PTRACE_PEEKUSR et al.  The diff is a little long but
it's almost all just fixing some bad constants/arithmetic.  Tested against
the peekpokeusr test case in the ptrace-tests suite, happy both 64 and 31.

Thanks,
Roland

diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c
index f5edc55..2fa708c 100644
--- a/arch/s390/kernel/ptrace.c
+++ b/arch/s390/kernel/ptrace.c
@@ -172,13 +172,15 @@ genregs_set(struct task_struct *target,
 		unsigned long pswmask = regs->psw.mask;
 		ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
 					   &pswmask, PT_PSWMASK, PT_PSWADDR);
-		if (pswmask != PSW_MASK_MERGE(PSW_USER_BITS, pswmask)
+		if (ret == 0 &&
 #ifdef CONFIG_COMPAT
-		    && pswmask != PSW_MASK_MERGE(PSW_USER32_BITS, pswmask)
+		    pswmask != PSW_MASK_MERGE(PSW_USER32_BITS, pswmask) &&
 #endif
-			)
+		    pswmask != PSW_MASK_MERGE(PSW_USER_BITS, pswmask))
 			/* Invalid psw mask. */
-			return -EINVAL;
+			ret = -EINVAL;
+		if (ret)
+			return ret;
 		regs->psw.mask = pswmask;
 		FixPerRegisters(target);
 	}
@@ -188,6 +190,8 @@ genregs_set(struct task_struct *target,
 		ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
 					   &regs->psw.addr, PT_PSWADDR,
 					   PT_ACR0);
+		if (ret)
+			return ret;
 #ifndef CONFIG_64BIT
 		/* I'd like to reject addresses without the
 		   high order bit but older gdb's rely on it */
@@ -241,23 +245,23 @@ fpregs_set(struct task_struct *target,
 		save_fp_regs(&target->thread.fp_regs);
 
 	/* If setting FPC, must validate it first. */
-	if (count > 0 && pos == 0) {
-		unsigned long fpc;
+	if (count > 0 && pos < offsetof(s390_fp_regs, fprs)) {
+		u32 fpc[2] = { target->thread.fp_regs.fpc, 0 };
+		BUILD_BUG_ON(offsetof(s390_fp_regs, fprs) != sizeof(fpc));
 		ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
 					   &fpc, 0, sizeof(fpc));
 		if (ret)
 			return ret;
 
-		if ((fpc & ~((unsigned long) FPC_VALID_MASK
-			     << (BITS_PER_LONG - 32))) != 0)
+		if ((fpc[0] & ~FPC_VALID_MASK) != 0 || fpc[1] != 0)
 			return -EINVAL;
 
-		memcpy(&target->thread.fp_regs, &fpc, sizeof(fpc));
+		target->thread.fp_regs.fpc = fpc[0];
 	}
 
-	if (ret == 0)
+	if (ret == 0 && count > 0)
 		ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
-					   &target->thread.fp_regs, 0, -1);
+					   target->thread.fp_regs.fprs, 0, -1);
 
 	if (ret == 0 && target == current)
 		restore_fp_regs(&target->thread.fp_regs);
@@ -328,25 +332,31 @@ s390_genregs_get(struct task_struct *target,
 	int ret = 0;
 
 	/* Fake a 31 bit psw mask. */
-	if (count > 0 && pos == PT_PSWMASK / 2) {
+	if (count > 0 && pos == offsetof(struct user_regs_struct32, psw.mask)) {
 		u32 pswmask = PSW32_MASK_MERGE(PSW32_USER_BITS,
 					       (u32) (regs->psw.mask >> 32));
-		ret = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
-					    &pswmask, PT_PSWMASK / 2,
-					    PT_PSWADDR / 2);
+		ret = utrace_regset_copyout(
+			&pos, &count, &kbuf, &ubuf, &pswmask,
+			offsetof(struct user_regs_struct32, psw.mask),
+			offsetof(struct user_regs_struct32, psw.addr));
 	}
 
 	/* Fake a 31 bit psw address. */
-	if (ret == 0 && count > 0 && pos == PT_PSWADDR / 2) {
+	if (ret == 0 && count > 0 &&
+	    pos == offsetof(struct user_regs_struct32, psw.addr)) {
 		u32 pswaddr = (u32) regs->psw.addr | PSW32_ADDR_AMODE31;
-		ret = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
-					    &pswaddr, PT_PSWADDR / 2,
-					    PT_GPR0 / 2);
+		ret = utrace_regset_copyout(
+			&pos, &count, &kbuf, &ubuf, &pswaddr,
+			offsetof(struct user_regs_struct32, psw.addr),
+			offsetof(struct user_regs_struct32, gprs[0]));
 	}
 
 	/* The GPRs are directly on the stack.  Just truncate them.  */
-	while (ret == 0 && count > 0 && pos < PT_ACR0 / 2) {
-		u32 value = regs->gprs[(pos - PT_GPR0 / 2) / sizeof(u32)];
+	while (ret == 0 && count > 0 &&
+	       pos < offsetof(struct user_regs_struct32, acrs[0])) {
+		unsigned int n =
+			pos - offsetof(struct user_regs_struct32, gprs[0]);
+		u32 value = regs->gprs[n / sizeof(u32)];
 		if (kbuf) {
 			*(u32 *) kbuf = value;
 			kbuf += sizeof(u32);
@@ -360,22 +370,21 @@ s390_genregs_get(struct task_struct *target,
 	}
 
 	/* The ACRs are kept in the thread_struct.  */
-	if (ret == 0 && count > 0 && pos < PT_ACR0 / 2 + NUM_ACRS * ACR_SIZE) {
+	if (ret == 0 && count > 0 &&
+	    pos < offsetof(struct user_regs_struct32, acrs[NUM_ACRS])) {
 		if (target == current)
 			save_access_regs(target->thread.acrs);
-
-		ret = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
-					    target->thread.acrs,
-					    PT_ACR0 / 2,
-					    PT_ACR0 / 2 + NUM_ACRS * ACR_SIZE);
+		ret = utrace_regset_copyout(
+			&pos, &count, &kbuf, &ubuf, target->thread.acrs,
+			offsetof(struct user_regs_struct32, acrs[0]),
+			offsetof(struct user_regs_struct32, acrs[NUM_ACRS]));
 	}
 
 	/* Finally, the ORIG_GPR2 value.  */
 	if (count > 0) {
 		if (kbuf)
 			*(u32 *) kbuf = regs->orig_gpr2;
-		else if (put_user((u32) regs->orig_gpr2,
-				  (u32 __user *) ubuf))
+		else if (put_user((u32) regs->orig_gpr2, (u32 __user *) ubuf))
 			return -EFAULT;
 	}
 
@@ -392,15 +401,16 @@ s390_genregs_set(struct task_struct *target,
 	int ret = 0;
 
 	/* Check for an invalid PSW mask.  */
-	if (count > 0 && pos == PT_PSWMASK / 2) {
+	if (count > 0 && pos == offsetof(struct user_regs_struct32, psw.mask)) {
 		u32 pswmask;
-		ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
-					   &pswmask, PT_PSWMASK / 2,
-					   PT_PSWADDR / 2);
+		ret = utrace_regset_copyin(
+			&pos, &count, &kbuf, &ubuf, &pswmask,
+			offsetof(struct user_regs_struct32, psw.mask),
+			offsetof(struct user_regs_struct32, psw.addr));
 		if (ret)
 			return ret;
 
-		if (pswmask != PSW_MASK_MERGE(PSW_USER32_BITS, pswmask))
+		if (pswmask != PSW32_MASK_MERGE(PSW32_USER_BITS, pswmask))
 			/* Invalid psw mask. */
 			return -EINVAL;
 
@@ -411,18 +421,22 @@ s390_genregs_set(struct task_struct *target,
 	}
 
 	/* Build a 64 bit psw address from 31 bit address. */
-	if (count > 0 && pos == PT_PSWADDR / 2) {
+	if (count > 0 && pos == offsetof(struct user_regs_struct32, psw.addr)) {
 		u32 pswaddr;
-		ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
-					   &pswaddr, PT_PSWADDR / 2,
-					   PT_GPR0 / 2);
+		ret = utrace_regset_copyin(
+			&pos, &count, &kbuf, &ubuf, &pswaddr,
+			offsetof(struct user_regs_struct32, psw.addr),
+			offsetof(struct user_regs_struct32, gprs[0]));
 		if (ret == 0)
 			/* Build a 64 bit psw mask from 31 bit mask. */
 			regs->psw.addr = pswaddr & PSW32_ADDR_INSN;
 	}
 
 	/* The GPRs are directly onto the stack. */
-	while (ret == 0 && count > 0 && pos < PT_ACR0 / 2) {
+	while (ret == 0 && count > 0 &&
+	       pos < offsetof(struct user_regs_struct32, acrs[0])) {
+		unsigned int n =
+			pos - offsetof(struct user_regs_struct32, gprs[0]);
 		u32 value;
 
 		if (kbuf) {
@@ -436,20 +450,21 @@ s390_genregs_set(struct task_struct *target,
 		pos += sizeof(u32);
 		count -= sizeof(u32);
 
-		regs->gprs[(pos - PT_GPR0 / 2) / sizeof(u32)] = value;
+		regs->gprs[n / sizeof(u32)] = value;
 	}
 
 	/* The ACRs are kept in the thread_struct.  */
-	if (count > 0 && pos < PT_ORIGGPR2 / 2) {
-		if (target == current
-		    && (pos != PT_ACR0 / 2
-			|| count < sizeof(target->thread.acrs)))
+	if (count > 0 &&
+	    pos < offsetof(struct user_regs_struct32, acrs[NUM_ACRS])) {
+		if (target == current &&
+		    (pos != offsetof(struct user_regs_struct32, acrs[0]) ||
+		     count < sizeof(target->thread.acrs)))
 			save_access_regs(target->thread.acrs);
 
-		ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
-					   target->thread.acrs,
-					   PT_ACR0 / 2,
-					   PT_ACR0 / 2 + NUM_ACRS * ACR_SIZE);
+		ret = utrace_regset_copyin(
+			&pos, &count, &kbuf, &ubuf, target->thread.acrs,
+			offsetof(struct user_regs_struct32, acrs[0]),
+			offsetof(struct user_regs_struct32, acrs[NUM_ACRS]));
 
 		if (ret == 0 && target == current)
 			restore_access_regs(target->thread.acrs);
@@ -544,7 +559,7 @@ s390_per_info_set(struct task_struct *target,
 static const struct utrace_regset s390_compat_regsets[] = {
 	{
 		.size = sizeof(u32), .align = sizeof(u32),
-		.n = sizeof(s390_regs) / sizeof(long),
+		.n = offsetof(struct user_regs_struct32, fp_regs) / sizeof(u32),
 		.get = s390_genregs_get, .set = s390_genregs_set
 	},
 	{
@@ -680,11 +695,15 @@ int arch_ptrace(long *request, struct task_struct *child,
 
 #ifdef CONFIG_COMPAT
 static const struct ptrace_layout_segment s390_compat_uarea[] = {
-	{PT_PSWMASK / 2, PT_FPC / 2, 0, 0},
-	{PT_FPC / 2, PT_CR_9 / 2, 1, 0},
-	{PT_CR_9 / 2, PT_IEEE_IP / 2, 2, 0},
-	{PT_IEEE_IP / 2, sizeof(struct user32), -1, -1},
-	{0, 0, -1, 0}
+	{ offsetof(struct user_regs_struct32, psw),
+	  offsetof(struct user_regs_struct32, fp_regs), 0, 0 },
+	{ offsetof(struct user_regs_struct32, fp_regs),
+	  offsetof(struct user_regs_struct32, per_info), 1, 0 },
+	{ offsetof(struct user_regs_struct32, per_info),
+	  offsetof(struct user_regs_struct32, ieee_instruction_pointer), 2, 0 },
+	{ offsetof(struct user_regs_struct32, ieee_instruction_pointer),
+	  sizeof(struct user32), -1, -1 },
+	{ 0, 0, -1, 0 }
 };
 
 int arch_compat_ptrace(compat_long_t *request,