Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

[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>

authored by

Martin Schwidefsky and committed by
Linus Torvalds
778959db 854715be

+43 -5
+43 -5
arch/s390/kernel/ptrace.c
··· 40 40 #include <asm/pgalloc.h> 41 41 #include <asm/system.h> 42 42 #include <asm/uaccess.h> 43 + #include <asm/unistd.h> 43 44 44 45 #ifdef CONFIG_S390_SUPPORT 45 46 #include "compat_ptrace.h" ··· 131 130 peek_user(struct task_struct *child, addr_t addr, addr_t data) 132 131 { 133 132 struct user *dummy = NULL; 134 - addr_t offset, tmp; 133 + addr_t offset, tmp, mask; 135 134 136 135 /* 137 136 * Stupid gdb peeks/pokes the access registers in 64 bit with 138 137 * an alignment of 4. Programmers from hell... 139 138 */ 140 - if ((addr & 3) || addr > sizeof(struct user) - __ADDR_MASK) 139 + mask = __ADDR_MASK; 140 + #ifdef CONFIG_ARCH_S390X 141 + if (addr >= (addr_t) &dummy->regs.acrs && 142 + addr < (addr_t) &dummy->regs.orig_gpr2) 143 + mask = 3; 144 + #endif 145 + if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK) 141 146 return -EIO; 142 147 143 148 if (addr < (addr_t) &dummy->regs.acrs) { ··· 160 153 * access registers are stored in the thread structure 161 154 */ 162 155 offset = addr - (addr_t) &dummy->regs.acrs; 156 + #ifdef CONFIG_ARCH_S390X 157 + /* 158 + * Very special case: old & broken 64 bit gdb reading 159 + * from acrs[15]. Result is a 64 bit value. Read the 160 + * 32 bit acrs[15] value and shift it by 32. Sick... 161 + */ 162 + if (addr == (addr_t) &dummy->regs.acrs[15]) 163 + tmp = ((unsigned long) child->thread.acrs[15]) << 32; 164 + else 165 + #endif 163 166 tmp = *(addr_t *)((addr_t) &child->thread.acrs + offset); 164 167 165 168 } else if (addr == (addr_t) &dummy->regs.orig_gpr2) { ··· 184 167 */ 185 168 offset = addr - (addr_t) &dummy->regs.fp_regs; 186 169 tmp = *(addr_t *)((addr_t) &child->thread.fp_regs + offset); 170 + if (addr == (addr_t) &dummy->regs.fp_regs.fpc) 171 + tmp &= (unsigned long) FPC_VALID_MASK 172 + << (BITS_PER_LONG - 32); 187 173 188 174 } else if (addr < (addr_t) (&dummy->regs.per_info + 1)) { 189 175 /* ··· 211 191 poke_user(struct task_struct *child, addr_t addr, addr_t data) 212 192 { 213 193 struct user *dummy = NULL; 214 - addr_t offset; 194 + addr_t offset, mask; 215 195 216 196 /* 217 197 * Stupid gdb peeks/pokes the access registers in 64 bit with 218 198 * an alignment of 4. Programmers from hell indeed... 219 199 */ 220 - if ((addr & 3) || addr > sizeof(struct user) - __ADDR_MASK) 200 + mask = __ADDR_MASK; 201 + #ifdef CONFIG_ARCH_S390X 202 + if (addr >= (addr_t) &dummy->regs.acrs && 203 + addr < (addr_t) &dummy->regs.orig_gpr2) 204 + mask = 3; 205 + #endif 206 + if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK) 221 207 return -EIO; 222 208 223 209 if (addr < (addr_t) &dummy->regs.acrs) { ··· 250 224 * access registers are stored in the thread structure 251 225 */ 252 226 offset = addr - (addr_t) &dummy->regs.acrs; 227 + #ifdef CONFIG_ARCH_S390X 228 + /* 229 + * Very special case: old & broken 64 bit gdb writing 230 + * to acrs[15] with a 64 bit value. Ignore the lower 231 + * half of the value and write the upper 32 bit to 232 + * acrs[15]. Sick... 233 + */ 234 + if (addr == (addr_t) &dummy->regs.acrs[15]) 235 + child->thread.acrs[15] = (unsigned int) (data >> 32); 236 + else 237 + #endif 253 238 *(addr_t *)((addr_t) &child->thread.acrs + offset) = data; 254 239 255 240 } else if (addr == (addr_t) &dummy->regs.orig_gpr2) { ··· 274 237 * floating point regs. are stored in the thread structure 275 238 */ 276 239 if (addr == (addr_t) &dummy->regs.fp_regs.fpc && 277 - (data & ~FPC_VALID_MASK) != 0) 240 + (data & ~((unsigned long) FPC_VALID_MASK 241 + << (BITS_PER_LONG - 32))) != 0) 278 242 return -EINVAL; 279 243 offset = addr - (addr_t) &dummy->regs.fp_regs; 280 244 *(addr_t *)((addr_t) &child->thread.fp_regs + offset) = data;