NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
kern/51514: ptrace(2) fails for 32-bit process on 64-bit kernel
>Number: 51514
>Category: kern
>Synopsis: ptrace(2) fails for 32-bit process on 64-bit kernel
>Confidential: no
>Severity: serious
>Priority: high
>Responsible: kern-bug-people
>State: open
>Class: change-request
>Submitter-Id: net
>Arrival-Date: Wed Sep 28 10:05:00 +0000 2016
>Originator: Rin Okuyama
>Release: 7.99.39
>Organization:
Faculty of Science and Technology, Keio University
>Environment:
NetBSD erlite 7.99.39 NetBSD 7.99.39 (ERLITE_RO) #5: Wed Sep 28 06:43:08 JST 2016 rin@XXX:XXX evbmips
>Description:
ptrace(2) does not work properly for 32-bit process on 64-bit kernel with
COMPAT_NETBSD32 option. As a result, GDB (both 64-bit and 32-bit version)
fails for 32-bit binaries. For example, on sparc64 machine with base.tgz
and comp.tgz from sparc extracted into /emul/netbsd32:
% /emul/netbsd32/usr/bin/cc -g -O0 hello.c
% /emul/netbsd32/usr/bin/gdb ./a.out
GNU gdb (GDB) 7.10.1
...snip...
(gdb) r
Starting program: /home/rin/a.out
Hello, World!
Then, it hangs indefinitely with a.out being a zombie. I observed similar
results on amd64 and mips64. ( For mips64, I reported the problem here:
http://mail-index.netbsd.org/port-mips/2016/09/11/msg000759.html )
This is especially problematic for mips64, for which the default userland
ABI is N32.
>How-To-Repeat:
described above
>Fix:
I added COMPAT_NETBSD32 support to kern/sys_process.c (In my opinion,
it is easier to implement it to kern/sys_process.c, rather than to
netbsd32_ptrace(9) in compat/netbsd32/netbsd32_netbsd.c). This requires
new functions, process_write_regs32 and process_write_fpregs32 for archs
in which "struct reg" and "struct reg32" ("fpreg" and "fpreg32") are not
equivalent. I added them to sys/ptrace.h, and implemented for amd64 and
sparc64. Then, 32-bit version of GDB works fine for 32-bit binaries, and
64-bit version fails but terminates properly. I tested the patch on
amd64, sparc64, and mips64 (with N32 userland).
--- src/sys/arch/amd64/amd64/netbsd32_machdep.c.orig 2016-09-27 23:02:35.595062549 +0900
+++ src/sys/arch/amd64/amd64/netbsd32_machdep.c 2016-09-28 06:40:34.097335905 +0900
@@ -487,12 +487,12 @@
{
struct trapframe *tf = l->l_md.md_regs;
- regs->r_gs = LSEL(LUCODE32_SEL, SEL_UPL);
- regs->r_fs = LSEL(LUCODE32_SEL, SEL_UPL);
- regs->r_es = LSEL(LUCODE32_SEL, SEL_UPL);
- regs->r_ds = LSEL(LUCODE32_SEL, SEL_UPL);
- regs->r_eflags = tf->tf_rflags;
/* XXX avoid sign extension problems with unknown upper bits? */
+ regs->r_gs = tf->tf_gs & 0xffff;
+ regs->r_fs = tf->tf_fs & 0xffff;
+ regs->r_es = tf->tf_es & 0xffff;
+ regs->r_ds = tf->tf_ds & 0xffff;
+ regs->r_eflags = tf->tf_rflags;
regs->r_edi = tf->tf_rdi & 0xffffffff;
regs->r_esi = tf->tf_rsi & 0xffffffff;
regs->r_ebp = tf->tf_rbp & 0xffffffff;
@@ -501,9 +501,9 @@
regs->r_ecx = tf->tf_rcx & 0xffffffff;
regs->r_eax = tf->tf_rax & 0xffffffff;
regs->r_eip = tf->tf_rip & 0xffffffff;
- regs->r_cs = tf->tf_cs;
+ regs->r_cs = tf->tf_cs & 0xffff;
regs->r_esp = tf->tf_rsp & 0xffffffff;
- regs->r_ss = tf->tf_ss;
+ regs->r_ss = tf->tf_ss & 0xffff;
return (0);
}
@@ -511,22 +511,52 @@
int
netbsd32_process_read_fpregs(struct lwp *l, struct fpreg32 *regs, size_t *sz)
{
- struct fpreg regs64;
- int error;
- size_t fp_size;
+
+ __CTASSERT(sizeof *regs == sizeof (struct save87));
+ process_read_fpregs_s87(l, (struct save87 *)regs);
+ return 0;
+}
+
+int
+netbsd32_process_write_regs(struct lwp *l, const struct reg32 *regs)
+{
+ struct trapframe *tf = l->l_md.md_regs;
/*
- * All that stuff makes no sense in i386 code :(
+ * Check for security violations. Taken from i386/process_machdep.c.
*/
+ if (((regs->r_eflags ^ tf->tf_rflags) & PSL_USERSTATIC) != 0 ||
+ !VALID_USER_CSEL32(regs->r_cs))
+ return EINVAL;
- fp_size = sizeof regs64;
- error = process_read_fpregs(l, ®s64, &fp_size);
- if (error)
- return error;
- __CTASSERT(sizeof *regs == sizeof (struct save87));
- process_xmm_to_s87(®s64.fxstate, (struct save87 *)regs);
+ tf->tf_rax = regs->r_eax;
+ tf->tf_rcx = regs->r_ecx;
+ tf->tf_rdx = regs->r_edx;
+ tf->tf_rbx = regs->r_ebx;
+ tf->tf_rsp = regs->r_esp;
+ tf->tf_rbp = regs->r_ebp;
+ tf->tf_rsi = regs->r_esi;
+ tf->tf_rdi = regs->r_edi;
+ tf->tf_rip = regs->r_eip;
+ tf->tf_rflags = regs->r_eflags;
+ tf->tf_cs = regs->r_cs;
+ tf->tf_ss = regs->r_ss;
+ tf->tf_ds = regs->r_ds;
+ tf->tf_es = regs->r_es;
+ tf->tf_fs = regs->r_fs;
+ tf->tf_gs = regs->r_gs;
- return (0);
+ return 0;
+}
+
+int
+netbsd32_process_write_fpregs(struct lwp *l, const struct fpreg32 *regs,
+ size_t sz)
+{
+
+ __CTASSERT(sizeof *regs == sizeof (struct save87));
+ process_write_fpregs_s87(l, (const struct save87 *)regs);
+ return 0;
}
int
--- src/sys/arch/amd64/include/ptrace.h.orig 2016-09-27 23:03:04.247552840 +0900
+++ src/sys/arch/amd64/include/ptrace.h 2016-09-27 23:03:44.910371215 +0900
@@ -68,6 +68,9 @@
#define process_read_regs32 netbsd32_process_read_regs
#define process_read_fpregs32 netbsd32_process_read_fpregs
+#define process_write_regs32 netbsd32_process_write_regs
+#define process_write_fpregs32 netbsd32_process_write_fpregs
+
#define process_reg32 struct reg32
#define process_fpreg32 struct fpreg32
#endif /* COMPAT_NETBSD32 */
--- src/sys/arch/amd64/include/netbsd32_machdep.h.orig 2016-09-27 23:07:53.849813992 +0900
+++ src/sys/arch/amd64/include/netbsd32_machdep.h 2016-09-28 00:01:52.104719432 +0900
@@ -138,4 +138,7 @@
int netbsd32_process_read_regs(struct lwp *, struct reg32 *);
int netbsd32_process_read_fpregs(struct lwp *, struct fpreg32 *, size_t *);
+int netbsd32_process_write_regs(struct lwp *, const struct reg32 *);
+int netbsd32_process_write_fpregs(struct lwp *, const struct fpreg32 *, size_t);
+
#endif /* _MACHINE_NETBSD32_H_ */
--- src/sys/arch/sparc64/include/ptrace.h.orig 2016-09-28 07:27:12.604872351 +0900
+++ src/sys/arch/sparc64/include/ptrace.h 2016-09-28 07:27:51.489447290 +0900
@@ -8,9 +8,12 @@
#ifdef COMPAT_NETBSD32
#include <compat/netbsd32/netbsd32.h>
-#define process_read_regs32 netbsd32_process_read_regs
+#define process_read_regs32 netbsd32_process_read_regs
#define process_read_fpregs32 netbsd32_process_read_fpregs
+#define process_write_regs32 netbsd32_process_write_regs
+#define process_write_fpregs32 netbsd32_process_write_fpregs
+
#define process_reg32 struct reg32
#define process_fpreg32 struct fpreg32
#endif
--- src/sys/arch/sparc64/include/netbsd32_machdep.h.orig 2016-09-28 07:28:21.564773071 +0900
+++ src/sys/arch/sparc64/include/netbsd32_machdep.h 2016-09-28 07:28:56.497298977 +0900
@@ -79,4 +79,7 @@
int netbsd32_process_read_regs(struct lwp *, struct reg32 *);
int netbsd32_process_read_fpregs(struct lwp *, struct fpreg32 *, size_t *);
+int netbsd32_process_write_regs(struct lwp *, const struct reg32 *);
+int netbsd32_process_write_fpregs(struct lwp *, const struct fpreg32 *, size_t);
+
#endif /* _MACHINE_NETBSD32_H_ */
--- src/sys/arch/sparc64/sparc64/netbsd32_machdep.c.orig 2016-09-28 07:29:43.583860712 +0900
+++ src/sys/arch/sparc64/sparc64/netbsd32_machdep.c 2016-09-28 11:56:11.368274396 +0900
@@ -624,16 +624,15 @@
return (0);
}
-#if 0
int
netbsd32_process_write_regs(struct lwp *l, const struct reg32 *regs)
{
- struct trapframe64* tf = p->p_md.md_tf;
+ struct trapframe64* tf = l->l_md.md_tf;
int i;
tf->tf_pc = regs->r_pc;
tf->tf_npc = regs->r_npc;
- tf->tf_y = regs->r_pc;
+ tf->tf_y = regs->r_y;
for (i = 0; i < 8; i++) {
tf->tf_global[i] = regs->r_global[i];
tf->tf_out[i] = regs->r_out[i];
@@ -643,7 +642,6 @@
PSRCC_TO_TSTATE(regs->r_psr);
return (0);
}
-#endif
int
netbsd32_process_read_fpregs(struct lwp *l, struct fpreg32 *regs, size_t *sz)
@@ -661,9 +659,9 @@
return 0;
}
-#if 0
int
-netbsd32_process_write_fpregs(struct lwp *l, const struct fpreg32 *regs)
+netbsd32_process_write_fpregs(struct lwp *l, const struct fpreg32 *regs,
+ size_t sz)
{
struct fpstate64 *statep;
int i;
@@ -678,7 +676,6 @@
return 0;
}
-#endif
/*
* 32-bit version of cpu_coredump.
--- src/sys/compat/netbsd32/netbsd32.h.orig 2016-09-27 13:11:26.515967512 +0900
+++ src/sys/compat/netbsd32/netbsd32.h 2016-09-27 14:07:52.164728060 +0900
@@ -282,6 +282,16 @@
/* from <sys/poll.h> */
typedef netbsd32_pointer_t netbsd32_pollfdp_t;
+/* from <sys/ptrace.h> */
+typedef netbsd32_pointer_t netbsd32_ptrace_io_descp_t;
+struct netbsd32_ptrace_io_desc {
+ int piod_op; /* I/O operation */
+ netbsd32_voidp piod_offs; /* child offset */
+ netbsd32_voidp piod_addr; /* parent offset */
+ netbsd32_size_t piod_len; /* request length (in) /
+ actual count (out) */
+};
+
/* from <sys/quotactl.h> */
typedef netbsd32_pointer_t netbsd32_quotactlargsp_t;
struct netbsd32_quotactlargs {
--- src/sys/kern/sys_process.c.orig 2016-09-27 11:57:58.429775526 +0900
+++ src/sys/kern/sys_process.c 2016-09-28 07:38:53.092742755 +0900
@@ -120,9 +120,12 @@
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: sys_process.c,v 1.169 2016/05/25 17:43:58 christos Exp $");
+#ifdef _KERNEL_OPT
#include "opt_ptrace.h"
#include "opt_ktrace.h"
#include "opt_pax.h"
+#include "opt_compat_netbsd32.h"
+#endif
#include <sys/param.h>
#include <sys/systm.h>
@@ -142,6 +145,10 @@
#include <machine/reg.h>
+#ifdef COMPAT_NETBSD32
+#include <compat/netbsd32/netbsd32.h>
+#endif
+
#ifdef PTRACE
# ifdef DEBUG
@@ -262,6 +269,10 @@
ksiginfo_t ksi;
char *path;
int len = 0;
+#ifdef COMPAT_NETBSD32
+ struct netbsd32_ptrace_io_desc piod32;
+ int p32 = p->p_flag & PK_32;
+#endif
error = 0;
req = SCARG(uap, req);
@@ -295,6 +306,18 @@
}
}
+#ifdef COMPAT_NETBSD32
+ /* The tracer and traced processes must have the same ABI. */
+ if (p32 != (t->p_flag & PK_32)) {
+ /* XXX for GDB to terminate processes of wrong ABI */
+ if (req != PT_KILL) {
+ mutex_exit(proc_lock);
+ mutex_exit(t->p_lock);
+ return EINVAL;
+ }
+ }
+#endif
+
/*
* Grab a reference on the process to prevent it from execing or
* exiting.
@@ -534,9 +557,23 @@
break;
case PT_IO:
- error = copyin(SCARG(uap, addr), &piod, sizeof(piod));
- if (error)
- break;
+#ifdef COMPAT_NETBSD32
+ if (p32) {
+ error = copyin(SCARG(uap, addr), &piod32,
+ sizeof(piod32));
+ if (error)
+ break;
+ piod.piod_op = piod32.piod_op;
+ piod.piod_offs = NETBSD32PTR64(piod32.piod_offs);
+ piod.piod_addr = NETBSD32PTR64(piod32.piod_addr);
+ piod.piod_len = (size_t)piod32.piod_len;
+ } else
+#endif
+ {
+ error = copyin(SCARG(uap, addr), &piod, sizeof(piod));
+ if (error)
+ break;
+ }
iov.iov_base = piod.piod_addr;
iov.iov_len = piod.piod_len;
@@ -563,7 +600,15 @@
case PIOD_READ_AUXV:
req = PT_READ_D;
uio.uio_rw = UIO_READ;
- tmp = t->p_execsw->es_arglen * sizeof(char *);
+ tmp = t->p_execsw->es_arglen;
+#ifdef COMPAT_NETBSD32
+ if (p32) {
+ tmp *= sizeof(netbsd32_pointer_t);
+ } else
+#endif
+ {
+ tmp *= sizeof(char *);
+ }
if (uio.uio_offset > tmp)
return EIO;
if (uio.uio_resid > tmp - uio.uio_offset)
@@ -586,7 +631,19 @@
error = process_domem(l, lt, &uio);
piod.piod_len -= uio.uio_resid;
- (void) copyout(&piod, SCARG(uap, addr), sizeof(piod));
+#ifdef COMPAT_NETBSD32
+ if (p32) {
+ piod32.piod_op = piod.piod_op;
+ NETBSD32PTR32(piod32.piod_offs, piod.piod_offs);
+ NETBSD32PTR32(piod32.piod_addr, piod.piod_addr);
+ piod32.piod_len = (netbsd32_size_t)piod.piod_len;
+ (void) copyout(&piod32, SCARG(uap, addr),
+ sizeof(piod32));
+ } else
+#endif
+ {
+ (void) copyout(&piod, SCARG(uap, addr), sizeof(piod));
+ }
uvmspace_free(vm);
break;
@@ -909,11 +966,18 @@
if (error)
break;
iov.iov_base = SCARG(uap, addr);
- iov.iov_len = sizeof(struct reg);
+#ifdef COMPAT_NETBSD32
+ if (p32) {
+ iov.iov_len = sizeof(process_reg32);
+ } else
+#endif
+ {
+ iov.iov_len = sizeof(struct reg);
+ }
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_offset = 0;
- uio.uio_resid = sizeof(struct reg);
+ uio.uio_resid = iov.iov_len;
uio.uio_rw = write ? UIO_WRITE : UIO_READ;
uio.uio_vmspace = vm;
@@ -952,11 +1016,18 @@
if (error)
break;
iov.iov_base = SCARG(uap, addr);
- iov.iov_len = sizeof(struct fpreg);
+#ifdef COMPAT_NETBSD32
+ if (p32) {
+ iov.iov_len = sizeof(process_fpreg32);
+ } else
+#endif
+ {
+ iov.iov_len = sizeof(struct fpreg);
+ }
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_offset = 0;
- uio.uio_resid = sizeof(struct fpreg);
+ uio.uio_resid = iov.iov_len;
uio.uio_rw = write ? UIO_WRITE : UIO_READ;
uio.uio_vmspace = vm;
@@ -995,26 +1066,54 @@
struct reg r;
char *kv;
int kl;
-
- if (uio->uio_offset < 0 || uio->uio_offset > (off_t)sizeof(r))
- return EINVAL;
-
- kl = sizeof(r);
- kv = (char *)&r;
+#ifdef COMPAT_NETBSD32
+ process_reg32 r32;
+ int p32 = l->l_proc->p_flag & PK_32; /* traced */
+#endif
+
+#ifdef COMPAT_NETBSD32
+ if (p32) {
+ if (uio->uio_offset < 0 || uio->uio_offset > (off_t)sizeof(r32))
+ return EINVAL;
+ kl = sizeof(r32);
+ kv = (char *)&r32;
+ } else
+#endif
+ {
+ if (uio->uio_offset < 0 || uio->uio_offset > (off_t)sizeof(r))
+ return EINVAL;
+ kl = sizeof(r);
+ kv = (char *)&r;
+ }
kv += uio->uio_offset;
kl -= uio->uio_offset;
if ((size_t)kl > uio->uio_resid)
kl = uio->uio_resid;
- error = process_read_regs(l, &r);
+#ifdef COMPAT_NETBSD32
+ if (p32) {
+ error = process_read_regs32(l, &r32);
+ } else
+#endif
+ {
+ error = process_read_regs(l, &r);
+ }
if (error == 0)
error = uiomove(kv, kl, uio);
if (error == 0 && uio->uio_rw == UIO_WRITE) {
if (l->l_stat != LSSTOP)
error = EBUSY;
- else
- error = process_write_regs(l, &r);
+ else {
+#ifdef COMPAT_NETBSD32
+ if (p32) {
+ error = process_write_regs32(l, &r32);
+ } else
+#endif
+ {
+ error = process_write_regs(l, &r);
+ }
+ }
}
uio->uio_offset = 0;
@@ -1045,26 +1144,54 @@
struct fpreg r;
char *kv;
size_t kl;
-
- if (uio->uio_offset < 0 || uio->uio_offset > (off_t)sizeof(r))
- return EINVAL;
-
- kl = sizeof(r);
- kv = (char *)&r;
+#ifdef COMPAT_NETBSD32
+ process_fpreg32 r32;
+ int p32 = l->l_proc->p_flag & PK_32; /* traced */
+#endif
+
+#ifdef COMPAT_NETBSD32
+ if (p32) {
+ if (uio->uio_offset < 0 || uio->uio_offset > (off_t)sizeof(r32))
+ return EINVAL;
+ kl = sizeof(r32);
+ kv = (char *)&r32;
+ } else
+#endif
+ {
+ if (uio->uio_offset < 0 || uio->uio_offset > (off_t)sizeof(r))
+ return EINVAL;
+ kl = sizeof(r);
+ kv = (char *)&r;
+ }
kv += uio->uio_offset;
kl -= uio->uio_offset;
if (kl > uio->uio_resid)
kl = uio->uio_resid;
- error = process_read_fpregs(l, &r, &kl);
+#ifdef COMPAT_NETBSD32
+ if (p32) {
+ error = process_read_fpregs32(l, &r32, &kl);
+ } else
+#endif
+ {
+ error = process_read_fpregs(l, &r, &kl);
+ }
if (error == 0)
error = uiomove(kv, kl, uio);
if (error == 0 && uio->uio_rw == UIO_WRITE) {
if (l->l_stat != LSSTOP)
error = EBUSY;
- else
- error = process_write_fpregs(l, &r, kl);
+ else {
+#ifdef COMPAT_NETBSD32
+ if (p32) {
+ error = process_write_fpregs32(l, &r32, kl);
+ } else
+#endif
+ {
+ error = process_write_fpregs(l, &r, kl);
+ }
+ }
}
uio->uio_offset = 0;
return (error);
--- src/sys/sys/ptrace.h.orig 2016-09-27 22:59:47.862486891 +0900
+++ src/sys/sys/ptrace.h 2016-09-28 05:46:26.502778292 +0900
@@ -180,9 +180,21 @@
int process_sstep(struct lwp *, int);
#ifdef PT_SETFPREGS
int process_write_fpregs(struct lwp *, const struct fpreg *, size_t);
+#ifndef process_write_fpregs32
+#define process_write_fpregs32 process_write_fpregs
+#endif
+#ifndef process_write_fpregs64
+#define process_write_fpregs64 process_write_fpregs
+#endif
#endif
#ifdef PT_SETREGS
int process_write_regs(struct lwp *, const struct reg *);
+#ifndef process_write_regs32
+#define process_write_regs32 process_write_regs
+#endif
+#ifndef process_write_regs64
+#define process_write_regs64 process_write_regs
+#endif
#endif
#ifdef __HAVE_PROCFS_MACHDEP
Home |
Main Index |
Thread Index |
Old Index