Subject: kern/21577: linux_sys_sendmsg() no worko
To: None <gnats-bugs@gnats.netbsd.org>
From: None <tv@pobox.com>
List: netbsd-bugs
Date: 05/14/2003 13:19:19
>Number:         21577
>Category:       kern
>Synopsis:       linux_sys_sendmsg() no worko
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed May 14 17:23:00 UTC 2003
>Closed-Date:
>Last-Modified:
>Originator:     Todd Vierling
>Release:        NetBSD 1.6.1_STABLE
>Organization:
	DUH.ORG:  Pointing out the obvious since 1994.
>Environment:
System: NetBSD server.duh.org 1.6.1_STABLE NetBSD 1.6.1_STABLE (HOME) #1: Wed May 14 12:33:57 EDT 2003 tv@server.duh.org:/export/SRC/duh/netbsd-kernels/HOME i386
Architecture: i386
Machine: i386
>Description:

[Technically this falls under "kern", not "port-i386", although there are
code comments that suggest that linux_sys_sendmsg() needs alignment fixups
in order to work at all on any other platform.]

The linux_sys_sendmsg() call does not work if given a msg_name of NULL, as
is typical when using SCM_RIGHTS to pass a fd from one side of a
socketpair to the other.  This results in an EFAULT when the real
sys_sendmsg() tries to get the msghdr.  A fix is provided (diff below).

Even after fixing this problem, passing fds results in EINVAL somewhere
after entering sys_sendmsg() -- regardless of whether msg_name is filled
in or not...!  I haven't been able to find what is causing the EINVAL yet,
so I pose this to others who may know the socket goop better than I.  (If
no one has immediate insight, I'll probably have to fire up a
debug-compiled kernel under VMware and start single-stepping.  8-)

>How-To-Repeat:

Sample program to illustrate the point.

Hint:  Yes, it looks similar to Wine's scheduler/client.c, as that is
precisely what I'm trying to fix.  Fixing this bug has a high probability
of making the bundled Wine in CrossOver Office work correctly under
emulation.

========== fdtest.c ==========
#include <err.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>

struct msghdr mh = {0};

struct cmsg_fd {
	int len;
	int level;
	int type;
	int fd;
} cm = { sizeof(cm), SOL_SOCKET, SCM_RIGHTS, -1 };

int main(void) {
	int fds[2];
	int buf = 1;
	struct iovec iov = {&buf, sizeof buf};

	if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds))
		err(1, "socketpair");

	warnx("sending fd %d", STDOUT_FILENO);

	cm.fd = STDOUT_FILENO;
	mh.msg_iov = &iov;
	mh.msg_iovlen = 1;
	mh.msg_control = &cm;
	mh.msg_controllen = sizeof(cm);

	if (sendmsg(fds[0], &mh, 0) < 0)
		err(1, "sendmsg");

	if (recvmsg(fds[1], &mh, 0) < 0)
		err(1, "recvmsg");

	warnx("received fd %d", cm.fd);

	write(5, "test output\n", 12);

	return 0;
}
==========

Expected output:

  fdtest: sending fd 1
  fdtest: received fd 5
  test output

Invalid Linux emulation output (before diff below):

  fdtest: sending fd 1
  fdtest: sendmsg: Bad address

Invalid Linux emulation output (after diff below):

  fdtest: sending fd 1
  fdtest: sendmsg: Invalid argument

>Fix:

The following diff fixes the msgbuf pointer problem by properly
initializing "nmsg" in the case that it is not reallocated in the stackgap
and rewritten.  This eliminates the erroneous EFAULT in sys_sendmsg().

I recommend committing this diff and pulling it up to 1.6, regardless of
the resolution of the pending EINVAL problem above.

===================================================================
--- compat/linux/common/linux_socket.c	2002/05/12 18:30:32	1.39
+++ compat/linux/common/linux_socket.c	2003/05/14 17:06:12
@@ -300,7 +300,7 @@
 	struct msghdr	msg;
 	int		error;
 	struct sys_sendmsg_args bsa;
-	struct msghdr *nmsg = NULL;
+	struct msghdr *nmsg = SCARG(uap, msg);
 
 	error = copyin(SCARG(uap, msg), (caddr_t)&msg, sizeof(msg));
 	if (error)
===================================================================
>Release-Note:
>Audit-Trail:
>Unformatted: