diff options
| author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2005-06-04 18:43:30 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-06-04 20:13:00 -0400 |
| commit | 778959db97c7ed8eed4025916916b17a4629ce3d (patch) | |
| tree | a1e901eac1c3d567df29cd87e76ff804b4753d83 /arch | |
| parent | 854715be73b221596c7127d4042e1120d4539e19 (diff) | |
[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>
Diffstat (limited to 'arch')
| -rw-r--r-- | arch/s390/kernel/ptrace.c | 48 |
1 files changed, 43 insertions, 5 deletions
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index 26889366929a..329d9391c83d 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c | |||
| @@ -40,6 +40,7 @@ | |||
| 40 | #include <asm/pgalloc.h> | 40 | #include <asm/pgalloc.h> |
| 41 | #include <asm/system.h> | 41 | #include <asm/system.h> |
| 42 | #include <asm/uaccess.h> | 42 | #include <asm/uaccess.h> |
| 43 | #include <asm/unistd.h> | ||
| 43 | 44 | ||
| 44 | #ifdef CONFIG_S390_SUPPORT | 45 | #ifdef CONFIG_S390_SUPPORT |
| 45 | #include "compat_ptrace.h" | 46 | #include "compat_ptrace.h" |
| @@ -130,13 +131,19 @@ static int | |||
| 130 | peek_user(struct task_struct *child, addr_t addr, addr_t data) | 131 | peek_user(struct task_struct *child, addr_t addr, addr_t data) |
| 131 | { | 132 | { |
| 132 | struct user *dummy = NULL; | 133 | struct user *dummy = NULL; |
| 133 | addr_t offset, tmp; | 134 | addr_t offset, tmp, mask; |
| 134 | 135 | ||
| 135 | /* | 136 | /* |
| 136 | * Stupid gdb peeks/pokes the access registers in 64 bit with | 137 | * Stupid gdb peeks/pokes the access registers in 64 bit with |
| 137 | * an alignment of 4. Programmers from hell... | 138 | * an alignment of 4. Programmers from hell... |
| 138 | */ | 139 | */ |
| 139 | if ((addr & 3) || addr > sizeof(struct user) - __ADDR_MASK) | 140 | mask = __ADDR_MASK; |
| 141 | #ifdef CONFIG_ARCH_S390X | ||
| 142 | if (addr >= (addr_t) &dummy->regs.acrs && | ||
| 143 | addr < (addr_t) &dummy->regs.orig_gpr2) | ||
| 144 | mask = 3; | ||
| 145 | #endif | ||
| 146 | if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK) | ||
| 140 | return -EIO; | 147 | return -EIO; |
| 141 | 148 | ||
| 142 | if (addr < (addr_t) &dummy->regs.acrs) { | 149 | if (addr < (addr_t) &dummy->regs.acrs) { |
| @@ -153,6 +160,16 @@ peek_user(struct task_struct *child, addr_t addr, addr_t data) | |||
| 153 | * access registers are stored in the thread structure | 160 | * access registers are stored in the thread structure |
| 154 | */ | 161 | */ |
| 155 | offset = addr - (addr_t) &dummy->regs.acrs; | 162 | offset = addr - (addr_t) &dummy->regs.acrs; |
| 163 | #ifdef CONFIG_ARCH_S390X | ||
| 164 | /* | ||
| 165 | * Very special case: old & broken 64 bit gdb reading | ||
| 166 | * from acrs[15]. Result is a 64 bit value. Read the | ||
| 167 | * 32 bit acrs[15] value and shift it by 32. Sick... | ||
| 168 | */ | ||
| 169 | if (addr == (addr_t) &dummy->regs.acrs[15]) | ||
| 170 | tmp = ((unsigned long) child->thread.acrs[15]) << 32; | ||
| 171 | else | ||
| 172 | #endif | ||
| 156 | tmp = *(addr_t *)((addr_t) &child->thread.acrs + offset); | 173 | tmp = *(addr_t *)((addr_t) &child->thread.acrs + offset); |
| 157 | 174 | ||
| 158 | } else if (addr == (addr_t) &dummy->regs.orig_gpr2) { | 175 | } 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) | |||
| 167 | */ | 184 | */ |
| 168 | offset = addr - (addr_t) &dummy->regs.fp_regs; | 185 | offset = addr - (addr_t) &dummy->regs.fp_regs; |
| 169 | tmp = *(addr_t *)((addr_t) &child->thread.fp_regs + offset); | 186 | tmp = *(addr_t *)((addr_t) &child->thread.fp_regs + offset); |
| 187 | if (addr == (addr_t) &dummy->regs.fp_regs.fpc) | ||
| 188 | tmp &= (unsigned long) FPC_VALID_MASK | ||
| 189 | << (BITS_PER_LONG - 32); | ||
| 170 | 190 | ||
| 171 | } else if (addr < (addr_t) (&dummy->regs.per_info + 1)) { | 191 | } else if (addr < (addr_t) (&dummy->regs.per_info + 1)) { |
| 172 | /* | 192 | /* |
| @@ -191,13 +211,19 @@ static int | |||
| 191 | poke_user(struct task_struct *child, addr_t addr, addr_t data) | 211 | poke_user(struct task_struct *child, addr_t addr, addr_t data) |
| 192 | { | 212 | { |
| 193 | struct user *dummy = NULL; | 213 | struct user *dummy = NULL; |
| 194 | addr_t offset; | 214 | addr_t offset, mask; |
| 195 | 215 | ||
| 196 | /* | 216 | /* |
| 197 | * Stupid gdb peeks/pokes the access registers in 64 bit with | 217 | * Stupid gdb peeks/pokes the access registers in 64 bit with |
| 198 | * an alignment of 4. Programmers from hell indeed... | 218 | * an alignment of 4. Programmers from hell indeed... |
| 199 | */ | 219 | */ |
| 200 | if ((addr & 3) || addr > sizeof(struct user) - __ADDR_MASK) | 220 | mask = __ADDR_MASK; |
| 221 | #ifdef CONFIG_ARCH_S390X | ||
| 222 | if (addr >= (addr_t) &dummy->regs.acrs && | ||
| 223 | addr < (addr_t) &dummy->regs.orig_gpr2) | ||
| 224 | mask = 3; | ||
| 225 | #endif | ||
| 226 | if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK) | ||
| 201 | return -EIO; | 227 | return -EIO; |
| 202 | 228 | ||
| 203 | if (addr < (addr_t) &dummy->regs.acrs) { | 229 | if (addr < (addr_t) &dummy->regs.acrs) { |
| @@ -224,6 +250,17 @@ poke_user(struct task_struct *child, addr_t addr, addr_t data) | |||
| 224 | * access registers are stored in the thread structure | 250 | * access registers are stored in the thread structure |
| 225 | */ | 251 | */ |
| 226 | offset = addr - (addr_t) &dummy->regs.acrs; | 252 | offset = addr - (addr_t) &dummy->regs.acrs; |
| 253 | #ifdef CONFIG_ARCH_S390X | ||
| 254 | /* | ||
| 255 | * Very special case: old & broken 64 bit gdb writing | ||
| 256 | * to acrs[15] with a 64 bit value. Ignore the lower | ||
| 257 | * half of the value and write the upper 32 bit to | ||
| 258 | * acrs[15]. Sick... | ||
| 259 | */ | ||
| 260 | if (addr == (addr_t) &dummy->regs.acrs[15]) | ||
| 261 | child->thread.acrs[15] = (unsigned int) (data >> 32); | ||
| 262 | else | ||
| 263 | #endif | ||
| 227 | *(addr_t *)((addr_t) &child->thread.acrs + offset) = data; | 264 | *(addr_t *)((addr_t) &child->thread.acrs + offset) = data; |
| 228 | 265 | ||
| 229 | } else if (addr == (addr_t) &dummy->regs.orig_gpr2) { | 266 | } 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) | |||
| 237 | * floating point regs. are stored in the thread structure | 274 | * floating point regs. are stored in the thread structure |
| 238 | */ | 275 | */ |
| 239 | if (addr == (addr_t) &dummy->regs.fp_regs.fpc && | 276 | if (addr == (addr_t) &dummy->regs.fp_regs.fpc && |
| 240 | (data & ~FPC_VALID_MASK) != 0) | 277 | (data & ~((unsigned long) FPC_VALID_MASK |
| 278 | << (BITS_PER_LONG - 32))) != 0) | ||
| 241 | return -EINVAL; | 279 | return -EINVAL; |
| 242 | offset = addr - (addr_t) &dummy->regs.fp_regs; | 280 | offset = addr - (addr_t) &dummy->regs.fp_regs; |
| 243 | *(addr_t *)((addr_t) &child->thread.fp_regs + offset) = data; | 281 | *(addr_t *)((addr_t) &child->thread.fp_regs + offset) = data; |
