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, &regs64, &fp_size);
-	if (error)
-		return error;
-	__CTASSERT(sizeof *regs == sizeof (struct save87));
-	process_xmm_to_s87(&regs64.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