]> Cypherpunks repositories - gostls13.git/commitdiff
net: don't use splice for unix{packet,gram} connections
authorAndrei Tudor Călin <mail@acln.ro>
Fri, 21 Sep 2018 01:32:47 +0000 (03:32 +0200)
committerBrad Fitzpatrick <bradfitz@golang.org>
Mon, 24 Sep 2018 17:25:15 +0000 (17:25 +0000)
As pointed out in the aftermath of CL 113997, splice is not supported
for SOCK_SEQPACKET or SOCK_DGRAM unix sockets. Don't call poll.Splice
in those cases.

Change-Id: Ieab18fb0ae706fdeb249e3f54d51a3292e3ead62
Reviewed-on: https://go-review.googlesource.com/136635
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
src/net/splice_linux.go
src/net/splice_test.go

index 8a4d55af62a895b5d9ae5623ba60a5e6f19d9717..69c3f65770f8a335f3b43796ef447db31a62f62c 100644 (file)
@@ -11,7 +11,7 @@ import (
 
 // splice transfers data from r to c using the splice system call to minimize
 // copies from and to userspace. c must be a TCP connection. Currently, splice
-// is only enabled if r is a TCP or Unix connection.
+// is only enabled if r is a TCP or a stream-oriented Unix connection.
 //
 // If splice returns handled == false, it has performed no work.
 func splice(c *netFD, r io.Reader) (written int64, err error, handled bool) {
@@ -28,6 +28,9 @@ func splice(c *netFD, r io.Reader) (written int64, err error, handled bool) {
        if tc, ok := r.(*TCPConn); ok {
                s = tc.fd
        } else if uc, ok := r.(*UnixConn); ok {
+               if uc.fd.net != "unix" {
+                       return 0, nil, false
+               }
                s = uc.fd
        } else {
                return 0, nil, false
index 93e8b1f8cc0a1d974ab1ebace01c1ef8b60ca095..4c300172c5e9e2b6d099de421f13ac691d92da79 100644 (file)
@@ -24,6 +24,8 @@ func TestSplice(t *testing.T) {
                t.Skip("skipping unix-to-tcp tests")
        }
        t.Run("unix-to-tcp", func(t *testing.T) { testSplice(t, "unix", "tcp") })
+       t.Run("no-unixpacket", testSpliceNoUnixpacket)
+       t.Run("no-unixgram", testSpliceNoUnixgram)
 }
 
 func testSplice(t *testing.T, upNet, downNet string) {
@@ -208,6 +210,56 @@ func testSpliceIssue25985(t *testing.T, upNet, downNet string) {
        wg.Wait()
 }
 
+func testSpliceNoUnixpacket(t *testing.T) {
+       clientUp, serverUp, err := spliceTestSocketPair("unixpacket")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer clientUp.Close()
+       defer serverUp.Close()
+       clientDown, serverDown, err := spliceTestSocketPair("tcp")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer clientDown.Close()
+       defer serverDown.Close()
+       // If splice called poll.Splice here, we'd get err == syscall.EINVAL
+       // and handled == false.  If poll.Splice gets an EINVAL on the first
+       // try, it assumes the kernel it's running on doesn't support splice
+       // for unix sockets and returns handled == false. This works for our
+       // purposes by somewhat of an accident, but is not entirely correct.
+       //
+       // What we want is err == nil and handled == false, i.e. we never
+       // called poll.Splice, because we know the unix socket's network.
+       _, err, handled := splice(serverDown.(*TCPConn).fd, serverUp)
+       if err != nil || handled != false {
+               t.Fatalf("got err = %v, handled = %t, want nil error, handled == false", err, handled)
+       }
+}
+
+func testSpliceNoUnixgram(t *testing.T) {
+       addr, err := ResolveUnixAddr("unixgram", testUnixAddr())
+       if err != nil {
+               t.Fatal(err)
+       }
+       up, err := ListenUnixgram("unixgram", addr)
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer up.Close()
+       clientDown, serverDown, err := spliceTestSocketPair("tcp")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer clientDown.Close()
+       defer serverDown.Close()
+       // Analogous to testSpliceNoUnixpacket.
+       _, err, handled := splice(serverDown.(*TCPConn).fd, up)
+       if err != nil || handled != false {
+               t.Fatalf("got err = %v, handled = %t, want nil error, handled == false", err, handled)
+       }
+}
+
 func BenchmarkSplice(b *testing.B) {
        testHookUninstaller.Do(uninstallTestHooks)