unix: in Linux sendmsgN actually send one normal byte

A bug in CL 412497 caused us to set up an iovec for a dummy byte
but fail to use it

Fixes golang/go#56384

Change-Id: If56fea5c47e5c6fac92f9707d4c3dd78f7999a65
Reviewed-on: https://go-review.googlesource.com/c/sys/+/445255
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: Tobias Klauser <tobias.klauser@gmail.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Run-TryBot: Ian Lance Taylor <iant@google.com>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
diff --git a/unix/syscall_linux.go b/unix/syscall_linux.go
index e044d5b..c5a9844 100644
--- a/unix/syscall_linux.go
+++ b/unix/syscall_linux.go
@@ -1554,6 +1554,7 @@
 				var iova [1]Iovec
 				iova[0].Base = &dummy
 				iova[0].SetLen(1)
+				iov = iova[:]
 			}
 		}
 		msg.Control = &oob[0]
diff --git a/unix/syscall_unix_test.go b/unix/syscall_unix_test.go
index afe2dc7..baff92e 100644
--- a/unix/syscall_unix_test.go
+++ b/unix/syscall_unix_test.go
@@ -1015,6 +1015,102 @@
 	}
 }
 
+// Issue 56384.
+func TestRecvmsgControl(t *testing.T) {
+	switch runtime.GOOS {
+	case "solaris", "illumos":
+		// Test fails on Solaris, saying
+		// "got 0 control messages, want 1".
+		// Not sure why; Solaris recvmsg man page says
+		// "For processes on the same host, recvmsg() can be
+		// used to receive a file descriptor from another
+		// process, but it cannot receive ancillary data."
+		t.Skipf("skipping on %s", runtime.GOOS)
+	}
+
+	fds, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_STREAM, 0)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer unix.Close(fds[0])
+	defer unix.Close(fds[1])
+
+	const payload = "hello"
+
+	// Start a goroutine that sends a control message followed by
+	// a payload on fds[1].
+	go func() {
+		f, err := os.Create(filepath.Join(t.TempDir(), "file"))
+		if err != nil {
+			t.Error(err)
+			return
+		}
+		defer f.Close()
+
+		rc, err := f.SyscallConn()
+		if err != nil {
+			t.Error(err)
+			return
+		}
+		var rights []byte
+		err = rc.Control(func(fd uintptr) {
+			rights = unix.UnixRights(int(fd))
+		})
+		if err != nil {
+			t.Error(err)
+			return
+		}
+
+		_, err = unix.SendmsgN(fds[1], nil, rights, nil, 0)
+		if err != nil {
+			t.Error(err)
+		}
+		if _, err := unix.Write(fds[1], []byte(payload)); err != nil {
+			t.Error(err)
+		}
+	}()
+
+	// Read the control message sent by the goroutine.  The
+	// goroutine writes to fds[1], we read from fds[0].
+
+	cbuf := make([]byte, unix.CmsgSpace(4))
+	_, cn, _, _, err := unix.Recvmsg(fds[0], nil, cbuf, 0)
+	if err != nil {
+		t.Fatal(err)
+	}
+	cbuf = cbuf[:cn]
+
+	// Read the payload sent by the goroutine.
+
+	buf := make([]byte, len(payload))
+	n, err := unix.Read(fds[0], buf)
+	if err != nil {
+		t.Fatal(err)
+	}
+	buf = buf[:n]
+	if payload != string(buf) {
+		t.Errorf("read payload %q, want %q", buf, payload)
+	}
+
+	// Check the control message.
+
+	cmsgs, err := unix.ParseSocketControlMessage(cbuf)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(cmsgs) != 1 {
+		t.Fatalf("got %d control messages, want 1", len(cmsgs))
+	}
+	cfds, err := unix.ParseUnixRights(&cmsgs[0])
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(cfds) != 1 {
+		t.Fatalf("got %d fds, want 1", len(cfds))
+	}
+	defer unix.Close(cfds[0])
+}
+
 // mktmpfifo creates a temporary FIFO and provides a cleanup function.
 func mktmpfifo(t *testing.T) (*os.File, func()) {
 	err := unix.Mkfifo("fifo", 0666)