From f905e456ed440e5e54c5aec6d2c42fa530f81f88 Mon Sep 17 00:00:00 2001 From: Nicolas Lacasse Date: Thu, 23 Feb 2023 12:54:04 -0800 Subject: [PATCH] hostinet: Generate socket validation and filters from list of supported sockets This cuts down on the amount of duplication between socket() syscall handling and the filters. No new functionality. PiperOrigin-RevId: 511862459 --- pkg/sentry/socket/hostinet/socket.go | 71 +++++++++++++-------- runsc/boot/filter/extra_filters_hostinet.go | 39 +++++------ 2 files changed, 60 insertions(+), 50 deletions(-) diff --git a/pkg/sentry/socket/hostinet/socket.go b/pkg/sentry/socket/hostinet/socket.go index b8dce12f5..1c013c2bd 100644 --- a/pkg/sentry/socket/hostinet/socket.go +++ b/pkg/sentry/socket/hostinet/socket.go @@ -48,6 +48,26 @@ const ( maxControlLen = 1024 ) +// AllowedSocketType is a tuple of socket family, type, and protocol. +type AllowedSocketType struct { + Family int + Type int + Protocol int +} + +// AllowedSocketTypes are the socket types which are supported by hostinet. +// These are used to validate the arguments to socket(), and also to generate +// syscall filters. +var AllowedSocketTypes = []AllowedSocketType{ + // Family, Type, Protocol. + {unix.AF_INET, unix.SOCK_STREAM, unix.IPPROTO_TCP}, + {unix.AF_INET, unix.SOCK_DGRAM, unix.IPPROTO_UDP}, + {unix.AF_INET, unix.SOCK_DGRAM, unix.IPPROTO_ICMP}, + + {unix.AF_INET6, unix.SOCK_STREAM, unix.IPPROTO_TCP}, + {unix.AF_INET6, unix.SOCK_DGRAM, unix.IPPROTO_UDP}, +} + // Socket implements socket.Socket (and by extension, vfs.FileDescriptionImpl) // for host sockets. // @@ -173,41 +193,36 @@ func (p *socketProvider) Socket(t *kernel.Task, stypeflags linux.SockType, proto return nil, nil } - // Only accept TCP and UDP. stype := stypeflags & linux.SOCK_TYPE_MASK - switch stype { - case unix.SOCK_STREAM: - switch protocol { - case unix.IPPROTO_IP: - // IPPROTO_IP with SOCK_STREAM causes proto to actually - // be IPPROTO_TCP. - protocol = unix.IPPROTO_TCP - case unix.IPPROTO_TCP: - // ok - default: - return nil, nil + + // Convert generic IPPROTO_IP protocol to the actual protocol depending + // on family and type. + if protocol == linux.IPPROTO_IP && (p.family == linux.AF_INET || p.family == linux.AF_INET6) { + switch stype { + case linux.SOCK_STREAM: + protocol = linux.IPPROTO_TCP + case linux.SOCK_DGRAM: + protocol = linux.IPPROTO_UDP } - case unix.SOCK_DGRAM: - switch protocol { - case unix.IPPROTO_IP: - // IPPROTO_IP with SOCK_DGRAM causes proto to actually - // be IPPROTO_UDP. - protocol = unix.IPPROTO_UDP - case unix.IPPROTO_ICMP: - // ok - case unix.IPPROTO_UDP: - // ok - default: - return nil, nil + } + + // Validate the socket based on family, type, and protocol. + var supported bool + for _, allowed := range AllowedSocketTypes { + if p.family == allowed.Family && int(stype) == allowed.Type && protocol == allowed.Protocol { + supported = true + break } - default: + } + if !supported { + // Return nil error here to give other socket providers a + // chance to create this socket. return nil, nil } // Conservatively ignore all flags specified by the application and add - // SOCK_NONBLOCK since socketOperations requires it. Pass a protocol of 0 - // to simplify the syscall filters, since 0 and IPPROTO_* are equivalent. - fd, err := unix.Socket(p.family, int(stype)|unix.SOCK_NONBLOCK|unix.SOCK_CLOEXEC, 0) + // SOCK_NONBLOCK since socketOperations requires it. + fd, err := unix.Socket(p.family, int(stype)|unix.SOCK_NONBLOCK|unix.SOCK_CLOEXEC, protocol) if err != nil { return nil, syserr.FromError(err) } diff --git a/runsc/boot/filter/extra_filters_hostinet.go b/runsc/boot/filter/extra_filters_hostinet.go index 458f5303a..ec9f8a3a7 100644 --- a/runsc/boot/filter/extra_filters_hostinet.go +++ b/runsc/boot/filter/extra_filters_hostinet.go @@ -16,6 +16,7 @@ package filter import ( "golang.org/x/sys/unix" + "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/seccomp" "gvisor.dev/gvisor/pkg/sentry/socket/hostinet" ) @@ -73,31 +74,24 @@ func hostInetFilters() seccomp.SyscallRules { seccomp.EqualTo(unix.SHUT_RDWR), }, }, - unix.SYS_SOCKET: []seccomp.Rule{ - { - seccomp.EqualTo(unix.AF_INET), - seccomp.EqualTo(unix.SOCK_STREAM | unix.SOCK_NONBLOCK | unix.SOCK_CLOEXEC), - seccomp.EqualTo(0), - }, - { - seccomp.EqualTo(unix.AF_INET), - seccomp.EqualTo(unix.SOCK_DGRAM | unix.SOCK_NONBLOCK | unix.SOCK_CLOEXEC), - seccomp.EqualTo(0), - }, - { - seccomp.EqualTo(unix.AF_INET6), - seccomp.EqualTo(unix.SOCK_STREAM | unix.SOCK_NONBLOCK | unix.SOCK_CLOEXEC), - seccomp.EqualTo(0), - }, - { - seccomp.EqualTo(unix.AF_INET6), - seccomp.EqualTo(unix.SOCK_DGRAM | unix.SOCK_NONBLOCK | unix.SOCK_CLOEXEC), - seccomp.EqualTo(0), - }, - }, unix.SYS_WRITEV: {}, } + // Generate rules for socket creation based on hostinet's supported + // socket types. + socketRules := []seccomp.Rule{} + for _, sock := range hostinet.AllowedSocketTypes { + socketRules = append(socketRules, seccomp.Rule{ + seccomp.EqualTo(sock.Family), + // We always set SOCK_NONBLOCK and SOCK_CLOEXEC + seccomp.EqualTo(sock.Type | linux.SOCK_NONBLOCK | linux.SOCK_CLOEXEC), + seccomp.EqualTo(sock.Protocol), + }) + } + rules[unix.SYS_SOCKET] = socketRules + + // Generate rules for socket options based on hostinet's supported + // socket options. getSockOptRules := []seccomp.Rule{} setSockOptRules := []seccomp.Rule{} for _, opt := range hostinet.SockOpts { @@ -128,5 +122,6 @@ func hostInetFilters() seccomp.SyscallRules { } rules[unix.SYS_GETSOCKOPT] = getSockOptRules rules[unix.SYS_SETSOCKOPT] = setSockOptRules + return rules }