parisc: fix bug in compat_arch_ptrace

Commit 81e192d6ce303b6792aa38ff35f41a1a7357f23a ("parisc: convert to
generic compat_sys_ptrace") introduced a bug which segfaults the parisc
64bit kernel when stracing 32bit applications:

Kernel Fault: Code=15 regs=00000000bafa42b0 (Addr=00000001baf5ab57)
YZrvWESTHLNXBCVMcbcbcbcbOGFRQPDI
PSW: 00001000000001101111111100001011 Tainted: G W
r00-03 000000ff0806ff0b 000000004068edc0 00000000401203f8 00000000fb3e2508
r04-07 0000000040686dc0 00000000baf5a800 fffffffffffffffc fffffffffb3e2508
r08-11 00000000baf5a800 000000000004b068 00000000000402b0 0000000000040d68
r12-15 0000000000042a9c 0000000000040a9c 0000000000040d60 0000000000042e9c
r16-19 000000000004b060 000000000004b058 0000000000042d9c ffffffffffffffff
r20-23 000000000800000b 0000000000000000 000000000800000b fffffffffb3e2508
r24-27 00000000fffffffc 0000000000000003 00000000fffffffc 0000000040686dc0
r28-31 00000001baf5a7ff 00000000bafa4280 00000000bafa42b0 00000000000001d7
sr00-03 0000000000fca000 0000000000000000 0000000000000000 0000000000fca000
sr04-07 0000000000000000 0000000000000000 0000000000000000 0000000000000000

IASQ: 0000000000000000 0000000000000000 IAOQ: 0000000040120400 0000000040120404
IIR: 4b9a06b0 ISR: 0000000000000000 IOR: 00000001baf5ab57
CPU: 0 CR30: 00000000bafa4000 CR31: 00000000d22344e0
ORIG_R28: 00000000fb3e2248
IAOQ[0]: compat_arch_ptrace+0xb8/0x160
IAOQ[1]: compat_arch_ptrace+0xbc/0x160
RP(r2): compat_arch_ptrace+0xb0/0x160
Backtrace:
[<00000000401612ac>] compat_sys_ptrace+0x15c/0x180
[<0000000040104ef8>] syscall_exit+0x0/0x14

The problem is that compat_arch_ptrace() enters with an addr value of
type compat_ulong_t and calls translate_usr_offset() to translate the
address offset into a struct pt_regs offset like this:

addr = translate_usr_offset(addr)

this means that any return value of translate_usr_offset() is stored
back as compat_ulong_t type into the addr variable.

But since translate_usr_offset() returns -1 for invalid offsets, addr
can now get the value 0xffffffff which then fails the next return-value
sanity check and thus the kernel tries to access invalid memory:

if (addr < 0)
break;

Fix this bug by modifying translate_usr_offset() to take and return
values of type compat_ulong_t, and by returning the value
"sizeof(struct pt_regs)" as an error indicator.

Additionally change the sanity check to check for return values
for >= sizeof(struct pt_regs).

This patch survived my compile and run-tests.

Signed-off-by: Helge Deller <deller@gmx.de>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by Helge Deller and committed by Linus Torvalds ed79b86d ee2f6cc7

+5 -5
+5 -5
arch/parisc/kernel/ptrace.c
··· 183 183 * being 64 bit in both cases. 184 184 */ 185 185 186 - static long translate_usr_offset(long offset) 186 + static compat_ulong_t translate_usr_offset(compat_ulong_t offset) 187 187 { 188 188 if (offset < 0) 189 - return -1; 189 + return sizeof(struct pt_regs); 190 190 else if (offset <= 32*4) /* gr[0..31] */ 191 191 return offset * 2 + 4; 192 192 else if (offset <= 32*4+32*8) /* gr[0..31] + fr[0..31] */ ··· 194 194 else if (offset < sizeof(struct pt_regs)/2 + 32*4) 195 195 return offset * 2 + 4 - 32*8; 196 196 else 197 - return -1; 197 + return sizeof(struct pt_regs); 198 198 } 199 199 200 200 long compat_arch_ptrace(struct task_struct *child, compat_long_t request, ··· 209 209 if (addr & (sizeof(compat_uint_t)-1)) 210 210 break; 211 211 addr = translate_usr_offset(addr); 212 - if (addr < 0) 212 + if (addr >= sizeof(struct pt_regs)) 213 213 break; 214 214 215 215 tmp = *(compat_uint_t *) ((char *) task_regs(child) + addr); ··· 236 236 if (addr & (sizeof(compat_uint_t)-1)) 237 237 break; 238 238 addr = translate_usr_offset(addr); 239 - if (addr < 0) 239 + if (addr >= sizeof(struct pt_regs)) 240 240 break; 241 241 if (addr >= PT_FR0 && addr <= PT_FR31 + 4) { 242 242 /* Special case, fp regs are 64 bits anyway */