diff options
author | HowJMay <vulxj0j8j8@gmail.com> | 2021-04-19 18:06:54 +0000 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2021-04-19 21:27:43 +0000 |
commit | e97d8eb027c0067f757860b6f766644de15941f2 (patch) | |
tree | 0293d4cafe26127f9563f56a8e11bbe1554046c0 /src/net | |
parent | bbb510ccc96e2ca1d0d1c91d244fba4e735d5a80 (diff) | |
download | go-e97d8eb027c0067f757860b6f766644de15941f2.tar.gz go-e97d8eb027c0067f757860b6f766644de15941f2.zip |
net: pass MSG_CMSG_CLOEXEC flag in ReadMsgUnix
As mentioned in #42765, calling "recvmsg" syscall on Linux should come
with "MSG_CMSG_CLOEXEC" flag.
For other systems which not supports "MSG_CMSG_CLOEXEC". ReadMsgUnix()
would check the header. If the header type is "syscall.SCM_RIGHTS",
then ReadMsgUnix() would parse the SocketControlMessage and call each
fd with "syscall.CloseOnExec"
Fixes #42765
Change-Id: I74347db72b465685d7684bf0f32415d285845ebb
GitHub-Last-Rev: ca59e2c9e0e8de1ae590e9b6dc165cb768a574f5
GitHub-Pull-Request: golang/go#42768
Reviewed-on: https://go-review.googlesource.com/c/go/+/272226
Trust: Emmanuel Odeke <emmanuel@orijtech.com>
Run-TryBot: Emmanuel Odeke <emmanuel@orijtech.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Tobias Klauser <tobias.klauser@gmail.com>
Diffstat (limited to 'src/net')
-rw-r--r-- | src/net/fd_posix.go | 6 | ||||
-rw-r--r-- | src/net/iprawsock_posix.go | 2 | ||||
-rw-r--r-- | src/net/net_fake.go | 2 | ||||
-rw-r--r-- | src/net/udpsock_posix.go | 2 | ||||
-rw-r--r-- | src/net/unixsock_posix.go | 6 | ||||
-rw-r--r-- | src/net/unixsock_readmsg_linux.go | 17 | ||||
-rw-r--r-- | src/net/unixsock_readmsg_other.go | 13 | ||||
-rw-r--r-- | src/net/unixsock_readmsg_posix.go | 33 | ||||
-rw-r--r-- | src/net/unixsock_readmsg_test.go | 105 |
9 files changed, 179 insertions, 7 deletions
diff --git a/src/net/fd_posix.go b/src/net/fd_posix.go index 2945e46a48..4703ff33a1 100644 --- a/src/net/fd_posix.go +++ b/src/net/fd_posix.go @@ -64,10 +64,10 @@ func (fd *netFD) readFrom(p []byte) (n int, sa syscall.Sockaddr, err error) { return n, sa, wrapSyscallError(readFromSyscallName, err) } -func (fd *netFD) readMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) { - n, oobn, flags, sa, err = fd.pfd.ReadMsg(p, oob) +func (fd *netFD) readMsg(p []byte, oob []byte, flags int) (n, oobn, retflags int, sa syscall.Sockaddr, err error) { + n, oobn, retflags, sa, err = fd.pfd.ReadMsg(p, oob, flags) runtime.KeepAlive(fd) - return n, oobn, flags, sa, wrapSyscallError(readMsgSyscallName, err) + return n, oobn, retflags, sa, wrapSyscallError(readMsgSyscallName, err) } func (fd *netFD) Write(p []byte) (nn int, err error) { diff --git a/src/net/iprawsock_posix.go b/src/net/iprawsock_posix.go index c1514f1698..b94eec0e18 100644 --- a/src/net/iprawsock_posix.go +++ b/src/net/iprawsock_posix.go @@ -75,7 +75,7 @@ func stripIPv4Header(n int, b []byte) int { func (c *IPConn) readMsg(b, oob []byte) (n, oobn, flags int, addr *IPAddr, err error) { var sa syscall.Sockaddr - n, oobn, flags, sa, err = c.fd.readMsg(b, oob) + n, oobn, flags, sa, err = c.fd.readMsg(b, oob, 0) switch sa := sa.(type) { case *syscall.SockaddrInet4: addr = &IPAddr{IP: sa.Addr[0:]} diff --git a/src/net/net_fake.go b/src/net/net_fake.go index 49dc57c6ff..74fc1da6fd 100644 --- a/src/net/net_fake.go +++ b/src/net/net_fake.go @@ -268,7 +268,7 @@ func (fd *netFD) readFrom(p []byte) (n int, sa syscall.Sockaddr, err error) { return 0, nil, syscall.ENOSYS } -func (fd *netFD) readMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) { +func (fd *netFD) readMsg(p []byte, oob []byte, flags int) (n, oobn, retflags int, sa syscall.Sockaddr, err error) { return 0, 0, 0, nil, syscall.ENOSYS } diff --git a/src/net/udpsock_posix.go b/src/net/udpsock_posix.go index 3b5346e573..fcfb9c004c 100644 --- a/src/net/udpsock_posix.go +++ b/src/net/udpsock_posix.go @@ -56,7 +56,7 @@ func (c *UDPConn) readFrom(b []byte, addr *UDPAddr) (int, *UDPAddr, error) { func (c *UDPConn) readMsg(b, oob []byte) (n, oobn, flags int, addr *UDPAddr, err error) { var sa syscall.Sockaddr - n, oobn, flags, sa, err = c.fd.readMsg(b, oob) + n, oobn, flags, sa, err = c.fd.readMsg(b, oob, 0) switch sa := sa.(type) { case *syscall.SockaddrInet4: addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port} diff --git a/src/net/unixsock_posix.go b/src/net/unixsock_posix.go index 1d1f27449f..0306b5989b 100644 --- a/src/net/unixsock_posix.go +++ b/src/net/unixsock_posix.go @@ -113,7 +113,11 @@ func (c *UnixConn) readFrom(b []byte) (int, *UnixAddr, error) { func (c *UnixConn) readMsg(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err error) { var sa syscall.Sockaddr - n, oobn, flags, sa, err = c.fd.readMsg(b, oob) + n, oobn, flags, sa, err = c.fd.readMsg(b, oob, readMsgFlags) + if oobn > 0 { + setReadMsgCloseOnExec(oob[:oobn]) + } + switch sa := sa.(type) { case *syscall.SockaddrUnix: if sa.Name != "" { diff --git a/src/net/unixsock_readmsg_linux.go b/src/net/unixsock_readmsg_linux.go new file mode 100644 index 0000000000..3296681017 --- /dev/null +++ b/src/net/unixsock_readmsg_linux.go @@ -0,0 +1,17 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux +// +build linux + +package net + +import ( + "syscall" +) + +const readMsgFlags = syscall.MSG_CMSG_CLOEXEC + +func setReadMsgCloseOnExec(oob []byte) { +} diff --git a/src/net/unixsock_readmsg_other.go b/src/net/unixsock_readmsg_other.go new file mode 100644 index 0000000000..c8db657cd6 --- /dev/null +++ b/src/net/unixsock_readmsg_other.go @@ -0,0 +1,13 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build (js && wasm) || windows +// +build js,wasm windows + +package net + +const readMsgFlags = 0 + +func setReadMsgCloseOnExec(oob []byte) { +} diff --git a/src/net/unixsock_readmsg_posix.go b/src/net/unixsock_readmsg_posix.go new file mode 100644 index 0000000000..07d7df5e66 --- /dev/null +++ b/src/net/unixsock_readmsg_posix.go @@ -0,0 +1,33 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd || solaris +// +build aix darwin dragonfly freebsd netbsd openbsd solaris + +package net + +import ( + "syscall" +) + +const readMsgFlags = 0 + +func setReadMsgCloseOnExec(oob []byte) { + scms, err := syscall.ParseSocketControlMessage(oob) + if err != nil { + return + } + + for _, scm := range scms { + if scm.Header.Level == syscall.SOL_SOCKET && scm.Header.Type == syscall.SCM_RIGHTS { + fds, err := syscall.ParseUnixRights(&scm) + if err != nil { + continue + } + for _, fd := range fds { + syscall.CloseOnExec(fd) + } + } + } +} diff --git a/src/net/unixsock_readmsg_test.go b/src/net/unixsock_readmsg_test.go new file mode 100644 index 0000000000..4961ecbe10 --- /dev/null +++ b/src/net/unixsock_readmsg_test.go @@ -0,0 +1,105 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris + +package net + +import ( + "os" + "syscall" + "testing" + "time" +) + +func TestUnixConnReadMsgUnixSCMRightsCloseOnExec(t *testing.T) { + if !testableNetwork("unix") { + t.Skip("not unix system") + } + + scmFile, err := os.Open(os.DevNull) + if err != nil { + t.Fatalf("file open: %v", err) + } + defer scmFile.Close() + + rights := syscall.UnixRights(int(scmFile.Fd())) + fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM, 0) + if err != nil { + t.Fatalf("Socketpair: %v", err) + } + + writeFile := os.NewFile(uintptr(fds[0]), "write-socket") + defer writeFile.Close() + readFile := os.NewFile(uintptr(fds[1]), "read-socket") + defer readFile.Close() + + cw, err := FileConn(writeFile) + if err != nil { + t.Fatalf("FileConn: %v", err) + } + defer cw.Close() + cr, err := FileConn(readFile) + if err != nil { + t.Fatalf("FileConn: %v", err) + } + defer cr.Close() + + ucw, ok := cw.(*UnixConn) + if !ok { + t.Fatalf("got %T; want UnixConn", cw) + } + ucr, ok := cr.(*UnixConn) + if !ok { + t.Fatalf("got %T; want UnixConn", cr) + } + + oob := make([]byte, syscall.CmsgSpace(4)) + err = ucw.SetWriteDeadline(time.Now().Add(5 * time.Second)) + if err != nil { + t.Fatalf("Can't set unix connection timeout: %v", err) + } + _, _, err = ucw.WriteMsgUnix(nil, rights, nil) + if err != nil { + t.Fatalf("UnixConn readMsg: %v", err) + } + err = ucr.SetReadDeadline(time.Now().Add(5 * time.Second)) + if err != nil { + t.Fatalf("Can't set unix connection timeout: %v", err) + } + _, oobn, _, _, err := ucr.ReadMsgUnix(nil, oob) + if err != nil { + t.Fatalf("UnixConn readMsg: %v", err) + } + + scms, err := syscall.ParseSocketControlMessage(oob[:oobn]) + if err != nil { + t.Fatalf("ParseSocketControlMessage: %v", err) + } + if len(scms) != 1 { + t.Fatalf("got scms = %#v; expected 1 SocketControlMessage", scms) + } + scm := scms[0] + gotFds, err := syscall.ParseUnixRights(&scm) + if err != nil { + t.Fatalf("syscall.ParseUnixRights: %v", err) + } + if len(gotFds) != 1 { + t.Fatalf("got FDs %#v: wanted only 1 fd", gotFds) + } + defer func() { + if err := syscall.Close(int(gotFds[0])); err != nil { + t.Fatalf("fail to close gotFds: %v", err) + } + }() + + flags, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(gotFds[0]), uintptr(syscall.F_GETFD), 0) + if errno != 0 { + t.Fatalf("Can't get flags of fd:%#v, with err:%v", gotFds[0], errno) + } + if flags&syscall.FD_CLOEXEC == 0 { + t.Fatalf("got flags %#x, want %#x (FD_CLOEXEC) set", flags, syscall.FD_CLOEXEC) + } +} |