summaryrefslogtreecommitdiffhomepage
path: root/pkg/tcpip/transport/unix
diff options
context:
space:
mode:
authorMichael Pratt <mpratt@google.com>2018-10-10 14:09:24 -0700
committerShentubot <shentubot@google.com>2018-10-10 14:10:17 -0700
commitddb34b3690c07f6c8efe2b96f89166145c4a7d3c (patch)
tree781361c955c356d26b484f572bc4ad41a250ab72 /pkg/tcpip/transport/unix
parentb78552d30e0af4122710e01bc86cbde6bb412686 (diff)
Enforce message size limits and avoid host calls with too many iovecs
Currently, in the face of FileMem fragmentation and a large sendmsg or recvmsg call, host sockets may pass > 1024 iovecs to the host, which will immediately cause the host to return EMSGSIZE. When we detect this case, use a single intermediate buffer to pass to the kernel, copying to/from the src/dst buffer. To avoid creating unbounded intermediate buffers, enforce message size checks and truncation w.r.t. the send buffer size. The same functionality is added to netstack unix sockets for feature parity. PiperOrigin-RevId: 216590198 Change-Id: I719a32e71c7b1098d5097f35e6daf7dd5190eff7
Diffstat (limited to 'pkg/tcpip/transport/unix')
-rw-r--r--pkg/tcpip/transport/unix/connectionless.go6
-rw-r--r--pkg/tcpip/transport/unix/unix.go49
2 files changed, 34 insertions, 21 deletions
diff --git a/pkg/tcpip/transport/unix/connectionless.go b/pkg/tcpip/transport/unix/connectionless.go
index ebd4802b0..ae93c61d7 100644
--- a/pkg/tcpip/transport/unix/connectionless.go
+++ b/pkg/tcpip/transport/unix/connectionless.go
@@ -105,14 +105,12 @@ func (e *connectionlessEndpoint) SendMsg(data [][]byte, c ControlMessages, to Bo
e.Lock()
n, notify, err := connected.Send(data, c, tcpip.FullAddress{Addr: tcpip.Address(e.path)})
e.Unlock()
- if err != nil {
- return 0, err
- }
+
if notify {
connected.SendNotify()
}
- return n, nil
+ return n, err
}
// Type implements Endpoint.Type.
diff --git a/pkg/tcpip/transport/unix/unix.go b/pkg/tcpip/transport/unix/unix.go
index 0bb00df42..718606cd1 100644
--- a/pkg/tcpip/transport/unix/unix.go
+++ b/pkg/tcpip/transport/unix/unix.go
@@ -260,20 +260,28 @@ type message struct {
Address tcpip.FullAddress
}
-// Length returns number of bytes stored in the Message.
+// Length returns number of bytes stored in the message.
func (m *message) Length() int64 {
return int64(len(m.Data))
}
-// Release releases any resources held by the Message.
+// Release releases any resources held by the message.
func (m *message) Release() {
m.Control.Release()
}
+// Peek returns a copy of the message.
func (m *message) Peek() queue.Entry {
return &message{Data: m.Data, Control: m.Control.Clone(), Address: m.Address}
}
+// Truncate reduces the length of the message payload to n bytes.
+//
+// Preconditions: n <= m.Length().
+func (m *message) Truncate(n int64) {
+ m.Data.CapLength(int(n))
+}
+
// A Receiver can be used to receive Messages.
type Receiver interface {
// Recv receives a single message. This method does not block.
@@ -623,23 +631,33 @@ func (e *connectedEndpoint) GetLocalAddress() (tcpip.FullAddress, *tcpip.Error)
// Send implements ConnectedEndpoint.Send.
func (e *connectedEndpoint) Send(data [][]byte, controlMessages ControlMessages, from tcpip.FullAddress) (uintptr, bool, *tcpip.Error) {
- var l int
+ var l int64
for _, d := range data {
- l += len(d)
- }
- // Discard empty stream packets. Since stream sockets don't preserve
- // message boundaries, sending zero bytes is a no-op. In Linux, the
- // receiver actually uses a zero-length receive as an indication that the
- // stream was closed.
- if l == 0 && e.endpoint.Type() == SockStream {
- controlMessages.Release()
- return 0, false, nil
+ l += int64(len(d))
+ }
+
+ truncate := false
+ if e.endpoint.Type() == SockStream {
+ // Since stream sockets don't preserve message boundaries, we
+ // can write only as much of the message as fits in the queue.
+ truncate = true
+
+ // Discard empty stream packets. Since stream sockets don't
+ // preserve message boundaries, sending zero bytes is a no-op.
+ // In Linux, the receiver actually uses a zero-length receive
+ // as an indication that the stream was closed.
+ if l == 0 {
+ controlMessages.Release()
+ return 0, false, nil
+ }
}
+
v := make([]byte, 0, l)
for _, d := range data {
v = append(v, d...)
}
- notify, err := e.writeQueue.Enqueue(&message{Data: buffer.View(v), Control: controlMessages, Address: from})
+
+ l, notify, err := e.writeQueue.Enqueue(&message{Data: buffer.View(v), Control: controlMessages, Address: from}, truncate)
return uintptr(l), notify, err
}
@@ -793,15 +811,12 @@ func (e *baseEndpoint) SendMsg(data [][]byte, c ControlMessages, to BoundEndpoin
n, notify, err := e.connected.Send(data, c, tcpip.FullAddress{Addr: tcpip.Address(e.path)})
e.Unlock()
- if err != nil {
- return 0, err
- }
if notify {
e.connected.SendNotify()
}
- return n, nil
+ return n, err
}
// SetSockOpt sets a socket option. Currently not supported.