unix: allow non-padded SockaddrIUCV

This was the intention from the start, but due to a logic error in the
handling of slices the implementation only handled minimum 8 character
strings.

This commit also improves the tests.

Change-Id: I6b0ed00bbd8a2faf90ca4a3ebe6218d3c5d6e8bf
GitHub-Last-Rev: 5b6dbc068212695d787788f1e8f71852a7cd407b
GitHub-Pull-Request: golang/sys#77
Reviewed-on: https://go-review.googlesource.com/c/sys/+/248778
Reviewed-by: Matt Layher <mdlayher@gmail.com>
Run-TryBot: Matt Layher <mdlayher@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/unix/syscall_internal_linux_test.go b/unix/syscall_internal_linux_test.go
index a690b99..7156c3d 100644
--- a/unix/syscall_internal_linux_test.go
+++ b/unix/syscall_internal_linux_test.go
@@ -474,6 +474,76 @@
 	}
 }
 
+func TestSockaddrIUCV_sockaddr(t *testing.T) {
+	tests := []struct {
+		name string
+		sa   *SockaddrIUCV
+		raw  *RawSockaddrIUCV
+		err  error
+	}{
+		{
+			name: "no fields set",
+			sa:   &SockaddrIUCV{},
+			raw: &RawSockaddrIUCV{
+				Family:  AF_IUCV,
+				Nodeid:  [8]int8{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
+				User_id: [8]int8{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
+				Name:    [8]int8{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
+			},
+		},
+		{
+			name: "both fields set",
+			sa: &SockaddrIUCV{
+				UserID: "USERID",
+				Name:   "NAME",
+			},
+			raw: &RawSockaddrIUCV{
+				Family:  AF_IUCV,
+				Nodeid:  [8]int8{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
+				User_id: [8]int8{'U', 'S', 'E', 'R', 'I', 'D', ' ', ' '},
+				Name:    [8]int8{'N', 'A', 'M', 'E', ' ', ' ', ' ', ' '},
+			},
+		},
+		{
+			name: "too long userid",
+			sa: &SockaddrIUCV{
+				UserID: "123456789",
+			},
+			err: EINVAL,
+		},
+		{
+			name: "too long name",
+			sa: &SockaddrIUCV{
+				Name: "123456789",
+			},
+			err: EINVAL,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			out, l, err := tt.sa.sockaddr()
+			if err != tt.err {
+				t.Fatalf("unexpected error: %v, want: %v", err, tt.err)
+			}
+
+			// Must be 0 on error or a fixed size otherwise.
+			if (tt.err != nil && l != 0) || (tt.raw != nil && l != SizeofSockaddrIUCV) {
+				t.Fatalf("unexpected Socklen: %d", l)
+			}
+			if out == nil {
+				// No pointer to cast, return early.
+				return
+			}
+
+			raw := (*RawSockaddrIUCV)(out)
+			if !reflect.DeepEqual(raw, tt.raw) {
+				t.Fatalf("unexpected RawSockaddrIUCV:\n got: %#v\nwant: %#v", raw, tt.raw)
+			}
+		})
+	}
+}
+
 // These helpers explicitly copy the contents of in into out to produce
 // the correct sockaddr structure, without relying on unsafe casting to
 // a type of a larger size.
diff --git a/unix/syscall_linux.go b/unix/syscall_linux.go
index e12f9ba..c48f5dd 100644
--- a/unix/syscall_linux.go
+++ b/unix/syscall_linux.go
@@ -902,10 +902,13 @@
 		sa.raw.User_id[i] = ' '
 		sa.raw.Name[i] = ' '
 	}
-	for i, b := range []byte(sa.UserID[:8]) {
+	if len(sa.UserID) > 8 || len(sa.Name) > 8 {
+		return nil, 0, EINVAL
+	}
+	for i, b := range []byte(sa.UserID[:]) {
 		sa.raw.User_id[i] = int8(b)
 	}
-	for i, b := range []byte(sa.Name[:8]) {
+	for i, b := range []byte(sa.Name[:]) {
 		sa.raw.Name[i] = int8(b)
 	}
 	return unsafe.Pointer(&sa.raw), SizeofSockaddrIUCV, nil