From 470997ca9990f2a985b7c29f5f27dc56b5fbec32 Mon Sep 17 00:00:00 2001
From: Ian Lewis <ianlewis@google.com>
Date: Thu, 10 Oct 2019 16:54:30 -0700
Subject: Allow for zero byte iovec with MSG_PEEK | MSG_TRUNC in recvmsg.

This allows for peeking at the length of the next message on a netlink socket
without pulling it off the socket's buffer/queue, allowing tools like 'ip' to
work.

This CL also fixes an issue where dump_done_errno was not included in the
NLMSG_DONE messages payload.

Issue #769

PiperOrigin-RevId: 274068637
---
 pkg/sentry/socket/netlink/BUILD     |  1 +
 pkg/sentry/socket/netlink/socket.go | 22 ++++++++++++++++++++++
 2 files changed, 23 insertions(+)

(limited to 'pkg/sentry/socket/netlink')

diff --git a/pkg/sentry/socket/netlink/BUILD b/pkg/sentry/socket/netlink/BUILD
index 45ebb2a0e..7da68384e 100644
--- a/pkg/sentry/socket/netlink/BUILD
+++ b/pkg/sentry/socket/netlink/BUILD
@@ -21,6 +21,7 @@ go_library(
         "//pkg/sentry/fs/fsutil",
         "//pkg/sentry/kernel",
         "//pkg/sentry/kernel/time",
+        "//pkg/sentry/safemem",
         "//pkg/sentry/socket",
         "//pkg/sentry/socket/netlink/port",
         "//pkg/sentry/socket/unix",
diff --git a/pkg/sentry/socket/netlink/socket.go b/pkg/sentry/socket/netlink/socket.go
index d0aab293d..b2732ca29 100644
--- a/pkg/sentry/socket/netlink/socket.go
+++ b/pkg/sentry/socket/netlink/socket.go
@@ -28,6 +28,7 @@ import (
 	"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
 	"gvisor.dev/gvisor/pkg/sentry/kernel"
 	ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
+	"gvisor.dev/gvisor/pkg/sentry/safemem"
 	"gvisor.dev/gvisor/pkg/sentry/socket"
 	"gvisor.dev/gvisor/pkg/sentry/socket/netlink/port"
 	"gvisor.dev/gvisor/pkg/sentry/socket/unix"
@@ -416,6 +417,24 @@ func (s *Socket) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags int, have
 		Peek:     flags&linux.MSG_PEEK != 0,
 	}
 
+	// If MSG_TRUNC is set with a zero byte destination then we still need
+	// to read the message and discard it, or in the case where MSG_PEEK is
+	// set, leave it be. In both cases the full message length must be
+	// returned. However, the memory manager for the destination will not read
+	// the endpoint if the destination is zero length.
+	//
+	// In order for the endpoint to be read when the destination size is zero,
+	// we must cause a read of the endpoint by using a separate fake zero
+	// length block sequence and calling the EndpointReader directly.
+	if trunc && dst.Addrs.NumBytes() == 0 {
+		// Perform a read to a zero byte block sequence. We can ignore the
+		// original destination since it was zero bytes. The length returned by
+		// ReadToBlocks is ignored and we return the full message length to comply
+		// with MSG_TRUNC.
+		_, err := r.ReadToBlocks(safemem.BlockSeqOf(safemem.BlockFromSafeSlice(make([]byte, 0))))
+		return int(r.MsgSize), linux.MSG_TRUNC, from, fromLen, socket.ControlMessages{}, syserr.FromError(err)
+	}
+
 	if n, err := dst.CopyOutFrom(t, &r); err != syserror.ErrWouldBlock || flags&linux.MSG_DONTWAIT != 0 {
 		var mflags int
 		if n < int64(r.MsgSize) {
@@ -499,6 +518,9 @@ func (s *Socket) sendResponse(ctx context.Context, ms *MessageSet) *syserr.Error
 			PortID: uint32(ms.PortID),
 		})
 
+		// Add the dump_done_errno payload.
+		m.Put(int64(0))
+
 		_, notify, err := s.connection.Send([][]byte{m.Finalize()}, transport.ControlMessages{}, tcpip.FullAddress{})
 		if err != nil && err != syserr.ErrWouldBlock {
 			return err
-- 
cgit v1.2.3