Subject: kern/8945: Calling pwrite() from Linux application causes panic
To: None <gnats-bugs@gnats.netbsd.org>
From: None <dave@dtsp.co.nz>
List: netbsd-bugs
Date: 12/04/1999 00:30:49
>Number:         8945
>Category:       kern
>Synopsis:       Calling pwrite() from Linux application causes panic
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    kern-bug-people (Kernel Bug People)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sat Dec  4 00:30:01 1999
>Last-Modified:
>Originator:     Dave Sainty
>Organization:
Dynamic Technology Services and Products Ltd (NZ)
>Release:        Recent current
>Environment:
i386, NetBSD-current
System: NetBSD tequila.dave.dtsp.co.nz 1.4P NetBSD 1.4P (TEQUILA) #7: Sat Dec  4 20:39:06 NZDT 1999     dave@tequila.dave.dtsp.co.nz:/vol/tequila/userB/u2/NetBSD-current/src/sys/arch/i386/compile/TEQUILA i386

>Description:

	pwrite() under linux emulation simply calls the NetBSD system call
	implementation.  linux_sysent.c contains:

	{ 4, sizeof(struct sys_pwrite_args), sys_pwrite }, /* 181 = pwrite */

	Now, sizeof(struct sys_pwrite_args) == 24, which presents a problem.
	See trap.c:

		if (linux) {
			/*
			 * Linux passes the args in ebx, ecx, edx, esi, edi, in
			 * increasing order.
			 */
			switch (argsize >> 2) {
			case 5:
				args[4] = frame.tf_edi;
			case 4:
				args[3] = frame.tf_esi;
			case 3:
				args[2] = frame.tf_edx;
			case 2:
				args[1] = frame.tf_ecx;
			case 1:
				args[0] = frame.tf_ebx;
				break;
			default:
				panic("linux syscall bogus argument size %d",
				    argsize);
				break;
			}
		}

	Because 24 >> 2 == 6, we panic.

>How-To-Repeat:
	In my case, run a linux netscape executable that tries to link with
	NetBSD X libraries.  Obviously anything that calls pwrite() will do
	it, but that's reasonably rare.

>Fix:

Here is "a" patch that allowed me to get past the panic and determine the real
problem.

I'm not sure if it is right though, does Linux use %ebp for the 7th block of
32 bits?  I chose it by a process of elimination (it was all that was left and
usable), but it may be that Linux simply cannot handle that much data in
arguments, in which case linux_sysent.c/syscalls.master is wrong about the
arguments.

Linux libc (from suse packages) doesn't implement it...
% nm /emul/linux.new/lib/libc.so.6 |fgrep -q pwrite;echo $?
1

So currently the problem is largely academic, except that it's a trivial
userland panic.

--- sys/arch/i386/i386/trap.c.orig	Sun Nov  7 09:30:38 1999
+++ sys/arch/i386/i386/trap.c	Sat Dec  4 20:38:33 1999
@@ -719,10 +719,12 @@
 #ifdef COMPAT_LINUX
 		if (linux) {
 			/*
-			 * Linux passes the args in ebx, ecx, edx, esi, edi, in
-			 * increasing order.
+			 * Linux passes the args in ebx, ecx, edx, esi, edi, ebp,
+			 * in increasing order.
 			 */
 			switch (argsize >> 2) {
+			case 6:
+				args[5] = frame.tf_ebp;
 			case 5:
 				args[4] = frame.tf_edi;
 			case 4:
>Audit-Trail:
>Unformatted: