Skip to content

Commit 93da0b6

Browse files
lucabianlancetaylor
authored andcommitted
syscall: drop dummy byte for oob in unixgram SendmsgN
This commit relaxes SendmsgN behavior of introducing a dummy 1-byte payload when sending ancillary-only messages. The fake payload is not needed for SOCK_DGRAM type sockets, and actually breaks interoperability with other fd-passing software (journald is one known example). This introduces an additional check to avoid injecting dummy payload in such case. Full reference at https:/golang.org/issue/6476#issue-51285243 Fixes #6476 Change-Id: I19a974b4e7920e002bd0556259ab766572358520 Reviewed-on: https://go-review.googlesource.com/45872 Reviewed-by: Ian Lance Taylor <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 5a7283f commit 93da0b6

File tree

2 files changed

+111
-86
lines changed

2 files changed

+111
-86
lines changed

src/syscall/creds_test.go

+99-84
Original file line numberDiff line numberDiff line change
@@ -19,101 +19,116 @@ import (
1919
// sockets. The SO_PASSCRED socket option is enabled on the sending
2020
// socket for this to work.
2121
func TestSCMCredentials(t *testing.T) {
22-
fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM, 0)
23-
if err != nil {
24-
t.Fatalf("Socketpair: %v", err)
22+
socketTypeTests := []struct {
23+
socketType int
24+
dataLen int
25+
}{
26+
{
27+
syscall.SOCK_STREAM,
28+
1,
29+
}, {
30+
syscall.SOCK_DGRAM,
31+
0,
32+
},
2533
}
26-
defer syscall.Close(fds[0])
27-
defer syscall.Close(fds[1])
2834

29-
err = syscall.SetsockoptInt(fds[0], syscall.SOL_SOCKET, syscall.SO_PASSCRED, 1)
30-
if err != nil {
31-
t.Fatalf("SetsockoptInt: %v", err)
32-
}
35+
for _, tt := range socketTypeTests {
36+
fds, err := syscall.Socketpair(syscall.AF_LOCAL, tt.socketType, 0)
37+
if err != nil {
38+
t.Fatalf("Socketpair: %v", err)
39+
}
40+
defer syscall.Close(fds[0])
41+
defer syscall.Close(fds[1])
3342

34-
srvFile := os.NewFile(uintptr(fds[0]), "server")
35-
defer srvFile.Close()
36-
srv, err := net.FileConn(srvFile)
37-
if err != nil {
38-
t.Errorf("FileConn: %v", err)
39-
return
40-
}
41-
defer srv.Close()
43+
err = syscall.SetsockoptInt(fds[0], syscall.SOL_SOCKET, syscall.SO_PASSCRED, 1)
44+
if err != nil {
45+
t.Fatalf("SetsockoptInt: %v", err)
46+
}
4247

43-
cliFile := os.NewFile(uintptr(fds[1]), "client")
44-
defer cliFile.Close()
45-
cli, err := net.FileConn(cliFile)
46-
if err != nil {
47-
t.Errorf("FileConn: %v", err)
48-
return
49-
}
50-
defer cli.Close()
48+
srvFile := os.NewFile(uintptr(fds[0]), "server")
49+
defer srvFile.Close()
50+
srv, err := net.FileConn(srvFile)
51+
if err != nil {
52+
t.Errorf("FileConn: %v", err)
53+
return
54+
}
55+
defer srv.Close()
56+
57+
cliFile := os.NewFile(uintptr(fds[1]), "client")
58+
defer cliFile.Close()
59+
cli, err := net.FileConn(cliFile)
60+
if err != nil {
61+
t.Errorf("FileConn: %v", err)
62+
return
63+
}
64+
defer cli.Close()
65+
66+
var ucred syscall.Ucred
67+
if os.Getuid() != 0 {
68+
ucred.Pid = int32(os.Getpid())
69+
ucred.Uid = 0
70+
ucred.Gid = 0
71+
oob := syscall.UnixCredentials(&ucred)
72+
_, _, err := cli.(*net.UnixConn).WriteMsgUnix(nil, oob, nil)
73+
if op, ok := err.(*net.OpError); ok {
74+
err = op.Err
75+
}
76+
if sys, ok := err.(*os.SyscallError); ok {
77+
err = sys.Err
78+
}
79+
if err != syscall.EPERM {
80+
t.Fatalf("WriteMsgUnix failed with %v, want EPERM", err)
81+
}
82+
}
5183

52-
var ucred syscall.Ucred
53-
if os.Getuid() != 0 {
5484
ucred.Pid = int32(os.Getpid())
55-
ucred.Uid = 0
56-
ucred.Gid = 0
85+
ucred.Uid = uint32(os.Getuid())
86+
ucred.Gid = uint32(os.Getgid())
5787
oob := syscall.UnixCredentials(&ucred)
58-
_, _, err := cli.(*net.UnixConn).WriteMsgUnix(nil, oob, nil)
59-
if op, ok := err.(*net.OpError); ok {
60-
err = op.Err
88+
89+
// On SOCK_STREAM, this is internally going to send a dummy byte
90+
n, oobn, err := cli.(*net.UnixConn).WriteMsgUnix(nil, oob, nil)
91+
if err != nil {
92+
t.Fatalf("WriteMsgUnix: %v", err)
6193
}
62-
if sys, ok := err.(*os.SyscallError); ok {
63-
err = sys.Err
94+
if n != 0 {
95+
t.Fatalf("WriteMsgUnix n = %d, want 0", n)
6496
}
65-
if err != syscall.EPERM {
66-
t.Fatalf("WriteMsgUnix failed with %v, want EPERM", err)
97+
if oobn != len(oob) {
98+
t.Fatalf("WriteMsgUnix oobn = %d, want %d", oobn, len(oob))
6799
}
68-
}
69-
70-
ucred.Pid = int32(os.Getpid())
71-
ucred.Uid = uint32(os.Getuid())
72-
ucred.Gid = uint32(os.Getgid())
73-
oob := syscall.UnixCredentials(&ucred)
74-
75-
// this is going to send a dummy byte
76-
n, oobn, err := cli.(*net.UnixConn).WriteMsgUnix(nil, oob, nil)
77-
if err != nil {
78-
t.Fatalf("WriteMsgUnix: %v", err)
79-
}
80-
if n != 0 {
81-
t.Fatalf("WriteMsgUnix n = %d, want 0", n)
82-
}
83-
if oobn != len(oob) {
84-
t.Fatalf("WriteMsgUnix oobn = %d, want %d", oobn, len(oob))
85-
}
86100

87-
oob2 := make([]byte, 10*len(oob))
88-
n, oobn2, flags, _, err := srv.(*net.UnixConn).ReadMsgUnix(nil, oob2)
89-
if err != nil {
90-
t.Fatalf("ReadMsgUnix: %v", err)
91-
}
92-
if flags != 0 {
93-
t.Fatalf("ReadMsgUnix flags = 0x%x, want 0", flags)
94-
}
95-
if n != 1 {
96-
t.Fatalf("ReadMsgUnix n = %d, want 1 (dummy byte)", n)
97-
}
98-
if oobn2 != oobn {
99-
// without SO_PASSCRED set on the socket, ReadMsgUnix will
100-
// return zero oob bytes
101-
t.Fatalf("ReadMsgUnix oobn = %d, want %d", oobn2, oobn)
102-
}
103-
oob2 = oob2[:oobn2]
104-
if !bytes.Equal(oob, oob2) {
105-
t.Fatal("ReadMsgUnix oob bytes don't match")
106-
}
101+
oob2 := make([]byte, 10*len(oob))
102+
n, oobn2, flags, _, err := srv.(*net.UnixConn).ReadMsgUnix(nil, oob2)
103+
if err != nil {
104+
t.Fatalf("ReadMsgUnix: %v", err)
105+
}
106+
if flags != 0 {
107+
t.Fatalf("ReadMsgUnix flags = 0x%x, want 0", flags)
108+
}
109+
if n != tt.dataLen {
110+
t.Fatalf("ReadMsgUnix n = %d, want %d", n, tt.dataLen)
111+
}
112+
if oobn2 != oobn {
113+
// without SO_PASSCRED set on the socket, ReadMsgUnix will
114+
// return zero oob bytes
115+
t.Fatalf("ReadMsgUnix oobn = %d, want %d", oobn2, oobn)
116+
}
117+
oob2 = oob2[:oobn2]
118+
if !bytes.Equal(oob, oob2) {
119+
t.Fatal("ReadMsgUnix oob bytes don't match")
120+
}
107121

108-
scm, err := syscall.ParseSocketControlMessage(oob2)
109-
if err != nil {
110-
t.Fatalf("ParseSocketControlMessage: %v", err)
111-
}
112-
newUcred, err := syscall.ParseUnixCredentials(&scm[0])
113-
if err != nil {
114-
t.Fatalf("ParseUnixCredentials: %v", err)
115-
}
116-
if *newUcred != ucred {
117-
t.Fatalf("ParseUnixCredentials = %+v, want %+v", newUcred, ucred)
122+
scm, err := syscall.ParseSocketControlMessage(oob2)
123+
if err != nil {
124+
t.Fatalf("ParseSocketControlMessage: %v", err)
125+
}
126+
newUcred, err := syscall.ParseUnixCredentials(&scm[0])
127+
if err != nil {
128+
t.Fatalf("ParseUnixCredentials: %v", err)
129+
}
130+
if *newUcred != ucred {
131+
t.Fatalf("ParseUnixCredentials = %+v, want %+v", newUcred, ucred)
132+
}
118133
}
119134
}

src/syscall/syscall_linux.go

+12-2
Original file line numberDiff line numberDiff line change
@@ -537,8 +537,13 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from
537537
}
538538
var dummy byte
539539
if len(oob) > 0 {
540+
var sockType int
541+
sockType, err = GetsockoptInt(fd, SOL_SOCKET, SO_TYPE)
542+
if err != nil {
543+
return
544+
}
540545
// receive at least one normal byte
541-
if len(p) == 0 {
546+
if sockType != SOCK_DGRAM && len(p) == 0 {
542547
iov.Base = &dummy
543548
iov.SetLen(1)
544549
}
@@ -584,8 +589,13 @@ func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error)
584589
}
585590
var dummy byte
586591
if len(oob) > 0 {
592+
var sockType int
593+
sockType, err = GetsockoptInt(fd, SOL_SOCKET, SO_TYPE)
594+
if err != nil {
595+
return 0, err
596+
}
587597
// send at least one normal byte
588-
if len(p) == 0 {
598+
if sockType != SOCK_DGRAM && len(p) == 0 {
589599
iov.Base = &dummy
590600
iov.SetLen(1)
591601
}

0 commit comments

Comments
 (0)