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/s390/kernel | |
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/s390/kernel')
-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; |