diff options
-rw-r--r-- | pkg/sentry/socket/netstack/netstack.go | 24 | ||||
-rw-r--r-- | pkg/sentry/socket/socket.go | 1 | ||||
-rw-r--r-- | pkg/tcpip/socketops.go | 2 | ||||
-rw-r--r-- | test/syscalls/linux/BUILD | 1 | ||||
-rw-r--r-- | test/syscalls/linux/socket_generic_test_cases.cc | 44 | ||||
-rw-r--r-- | test/util/capability_util.cc | 4 | ||||
-rw-r--r-- | test/util/capability_util.h | 5 |
7 files changed, 75 insertions, 6 deletions
diff --git a/pkg/sentry/socket/netstack/netstack.go b/pkg/sentry/socket/netstack/netstack.go index ea736e446..0f8cbe7e2 100644 --- a/pkg/sentry/socket/netstack/netstack.go +++ b/pkg/sentry/socket/netstack/netstack.go @@ -49,6 +49,7 @@ import ( "gvisor.dev/gvisor/pkg/sentry/fs/fsutil" "gvisor.dev/gvisor/pkg/sentry/inet" "gvisor.dev/gvisor/pkg/sentry/kernel" + "gvisor.dev/gvisor/pkg/sentry/kernel/auth" ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time" "gvisor.dev/gvisor/pkg/sentry/socket" "gvisor.dev/gvisor/pkg/sentry/socket/netfilter" @@ -1682,12 +1683,12 @@ func SetSockOpt(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, level int return nil } -func clampBufSize(newSz, min, max int64) int64 { +func clampBufSize(newSz, min, max int64, ignoreMax bool) int64 { // packetOverheadFactor is used to multiply the value provided by the user on // a setsockopt(2) for setting the send/receive buffer sizes sockets. const packetOverheadFactor = 2 - if newSz > max { + if !ignoreMax && newSz > max { newSz = max } @@ -1712,7 +1713,7 @@ func setSockOptSocket(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, nam v := hostarch.ByteOrder.Uint32(optVal) min, max := ep.SocketOptions().SendBufferLimits() - clamped := clampBufSize(int64(v), min, max) + clamped := clampBufSize(int64(v), min, max, false /* ignoreMax */) ep.SocketOptions().SetSendBufferSize(clamped, true /* notify */) return nil @@ -1723,7 +1724,22 @@ func setSockOptSocket(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, nam v := hostarch.ByteOrder.Uint32(optVal) min, max := ep.SocketOptions().ReceiveBufferLimits() - clamped := clampBufSize(int64(v), min, max) + clamped := clampBufSize(int64(v), min, max, false /* ignoreMax */) + ep.SocketOptions().SetReceiveBufferSize(clamped, true /* notify */) + return nil + + case linux.SO_RCVBUFFORCE: + if len(optVal) < sizeOfInt32 { + return syserr.ErrInvalidArgument + } + + if creds := auth.CredentialsFromContext(t); !creds.HasCapability(linux.CAP_NET_ADMIN) { + return syserr.ErrNotPermitted + } + + v := hostarch.ByteOrder.Uint32(optVal) + min, max := ep.SocketOptions().ReceiveBufferLimits() + clamped := clampBufSize(int64(v), min, max, true /* ignoreMax */) ep.SocketOptions().SetReceiveBufferSize(clamped, true /* notify */) return nil diff --git a/pkg/sentry/socket/socket.go b/pkg/sentry/socket/socket.go index f5da3c509..658e90bb9 100644 --- a/pkg/sentry/socket/socket.go +++ b/pkg/sentry/socket/socket.go @@ -509,7 +509,6 @@ func SetSockOptEmitUnimplementedEvent(t *kernel.Task, name int) { linux.SO_ATTACH_REUSEPORT_EBPF, linux.SO_CNX_ADVICE, linux.SO_DETACH_FILTER, - linux.SO_RCVBUFFORCE, linux.SO_SNDBUFFORCE: t.Kernel().EmitUnimplementedEvent(t) diff --git a/pkg/tcpip/socketops.go b/pkg/tcpip/socketops.go index 042326a3a..5642c86f8 100644 --- a/pkg/tcpip/socketops.go +++ b/pkg/tcpip/socketops.go @@ -54,7 +54,7 @@ type SocketOptionsHandler interface { // buffer size. It also returns the newly set value. OnSetSendBufferSize(v int64) (newSz int64) - // OnSetReceiveBufferSize is invoked to set the SO_RCVBUFSIZE. + // OnSetReceiveBufferSize is invoked by SO_RCVBUF and SO_RCVBUFFORCE. OnSetReceiveBufferSize(v, oldSz int64) (newSz int64) } diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index 984122bb5..fa2a080f1 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -2395,6 +2395,7 @@ cc_library( "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", gtest, + "//test/util:capability_util", "//test/util:test_util", ], alwayslink = 1, diff --git a/test/syscalls/linux/socket_generic_test_cases.cc b/test/syscalls/linux/socket_generic_test_cases.cc index 5c4cb6c35..fe5171bc8 100644 --- a/test/syscalls/linux/socket_generic_test_cases.cc +++ b/test/syscalls/linux/socket_generic_test_cases.cc @@ -14,6 +14,9 @@ #include "test/syscalls/linux/socket_generic.h" +#ifdef __linux__ +#include <linux/capability.h> +#endif // __linux__ #include <stdio.h> #include <sys/ioctl.h> #include <sys/socket.h> @@ -24,6 +27,7 @@ #include "absl/strings/string_view.h" #include "test/syscalls/linux/socket_test_util.h" #include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/capability_util.h" #include "test/util/test_util.h" // This file is a generic socket test file. It must be built with another file @@ -400,6 +404,46 @@ TEST_P(AllSocketPairTest, RcvBufSucceeds) { EXPECT_GT(size, 0); } +#ifdef __linux__ + +// Check that setting SO_RCVBUFFORCE above max is not clamped to the maximum +// receive buffer size. +TEST_P(AllSocketPairTest, SetSocketRecvBufForceAboveMax) { + std::unique_ptr<SocketPair> sockets = + ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + // Discover maxmimum buffer size by setting to a really large value. + constexpr int kRcvBufSz = 0xffffffff; + ASSERT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVBUF, &kRcvBufSz, + sizeof(kRcvBufSz)), + SyscallSucceeds()); + + int max = 0; + socklen_t max_len = sizeof(max); + ASSERT_THAT( + getsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVBUF, &max, &max_len), + SyscallSucceeds()); + + int above_max = max + 1; + int sso = setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVBUFFORCE, + &above_max, sizeof(above_max)); + if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))) { + ASSERT_THAT(sso, SyscallFailsWithErrno(EPERM)); + return; + } + ASSERT_THAT(sso, SyscallSucceeds()); + + int val = 0; + socklen_t val_len = sizeof(val); + ASSERT_THAT( + getsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVBUF, &val, &val_len), + SyscallSucceeds()); + // The system doubles the passed-in maximum. + ASSERT_EQ(above_max * 2, val); +} + +#endif // __linux__ + TEST_P(AllSocketPairTest, GetSndBufSucceeds) { auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); int size = 0; diff --git a/test/util/capability_util.cc b/test/util/capability_util.cc index a1b994c45..3bf218128 100644 --- a/test/util/capability_util.cc +++ b/test/util/capability_util.cc @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#ifdef __linux__ + #include "test/util/capability_util.h" #include <linux/capability.h> @@ -79,3 +81,5 @@ PosixErrorOr<bool> CanCreateUserNamespace() { } // namespace testing } // namespace gvisor + +#endif // __linux__ diff --git a/test/util/capability_util.h b/test/util/capability_util.h index f2c370125..c4b0feade 100644 --- a/test/util/capability_util.h +++ b/test/util/capability_util.h @@ -17,6 +17,8 @@ #ifndef GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_ #define GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_ +#ifdef __linux__ + #include <errno.h> #include <linux/capability.h> #include <sys/syscall.h> @@ -120,4 +122,7 @@ class AutoCapability { } // namespace testing } // namespace gvisor + +#endif // __linux__ + #endif // GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_ |