Add unsupported syscall events for get/setsockopt

PiperOrigin-RevId: 222148953
Change-Id: I21500a9f08939c45314a6414e0824490a973e5aa
This commit is contained in:
Fabricio Voznika
2018-11-20 14:04:12 -08:00
committed by Nicolas Lacasse
parent 237f9c7a5e
commit fadffa2ff8
11 changed files with 953 additions and 315 deletions
+1
View File
@@ -43,6 +43,7 @@ go_library(
"shm.go",
"signal.go",
"socket.go",
"tcp.go",
"time.go",
"timer.go",
"tty.go",
+107
View File
@@ -42,3 +42,110 @@ const (
IPPROTO_MPLS = 137
IPPROTO_RAW = 255
)
// Socket options from uapi/linux/in.h
const (
IP_TOS = 1
IP_TTL = 2
IP_HDRINCL = 3
IP_OPTIONS = 4
IP_ROUTER_ALERT = 5
IP_RECVOPTS = 6
IP_RETOPTS = 7
IP_PKTINFO = 8
IP_PKTOPTIONS = 9
IP_MTU_DISCOVER = 10
IP_RECVERR = 11
IP_RECVTTL = 12
IP_RECVTOS = 13
IP_MTU = 14
IP_FREEBIND = 15
IP_IPSEC_POLICY = 16
IP_XFRM_POLICY = 17
IP_PASSSEC = 18
IP_TRANSPARENT = 19
IP_ORIGDSTADDR = 20
IP_RECVORIGDSTADDR = IP_ORIGDSTADDR
IP_MINTTL = 21
IP_NODEFRAG = 22
IP_CHECKSUM = 23
IP_BIND_ADDRESS_NO_PORT = 24
IP_RECVFRAGSIZE = 25
IP_MULTICAST_IF = 32
IP_MULTICAST_TTL = 33
IP_MULTICAST_LOOP = 34
IP_ADD_MEMBERSHIP = 35
IP_DROP_MEMBERSHIP = 36
IP_UNBLOCK_SOURCE = 37
IP_BLOCK_SOURCE = 38
IP_ADD_SOURCE_MEMBERSHIP = 39
IP_DROP_SOURCE_MEMBERSHIP = 40
IP_MSFILTER = 41
MCAST_JOIN_GROUP = 42
MCAST_BLOCK_SOURCE = 43
MCAST_UNBLOCK_SOURCE = 44
MCAST_LEAVE_GROUP = 45
MCAST_JOIN_SOURCE_GROUP = 46
MCAST_LEAVE_SOURCE_GROUP = 47
MCAST_MSFILTER = 48
IP_MULTICAST_ALL = 49
IP_UNICAST_IF = 50
)
// Socket options from uapi/linux/in6.h
const (
IPV6_ADDRFORM = 1
IPV6_2292PKTINFO = 2
IPV6_2292HOPOPTS = 3
IPV6_2292DSTOPTS = 4
IPV6_2292RTHDR = 5
IPV6_2292PKTOPTIONS = 6
IPV6_CHECKSUM = 7
IPV6_2292HOPLIMIT = 8
IPV6_NEXTHOP = 9
IPV6_FLOWINFO = 11
IPV6_UNICAST_HOPS = 16
IPV6_MULTICAST_IF = 17
IPV6_MULTICAST_HOPS = 18
IPV6_MULTICAST_LOOP = 19
IPV6_ADD_MEMBERSHIP = 20
IPV6_DROP_MEMBERSHIP = 21
IPV6_ROUTER_ALERT = 22
IPV6_MTU_DISCOVER = 23
IPV6_MTU = 24
IPV6_RECVERR = 25
IPV6_V6ONLY = 26
IPV6_JOIN_ANYCAST = 27
IPV6_LEAVE_ANYCAST = 28
IPV6_MULTICAST_ALL = 29
IPV6_FLOWLABEL_MGR = 32
IPV6_FLOWINFO_SEND = 33
IPV6_IPSEC_POLICY = 34
IPV6_XFRM_POLICY = 35
IPV6_HDRINCL = 36
IPV6_RECVPKTINFO = 49
IPV6_PKTINFO = 50
IPV6_RECVHOPLIMIT = 51
IPV6_HOPLIMIT = 52
IPV6_RECVHOPOPTS = 53
IPV6_HOPOPTS = 54
IPV6_RTHDRDSTOPTS = 55
IPV6_RECVRTHDR = 56
IPV6_RTHDR = 57
IPV6_RECVDSTOPTS = 58
IPV6_DSTOPTS = 59
IPV6_RECVPATHMTU = 60
IPV6_PATHMTU = 61
IPV6_DONTFRAG = 62
IPV6_RECVTCLASS = 66
IPV6_TCLASS = 67
IPV6_AUTOFLOWLABEL = 70
IPV6_ADDR_PREFERENCES = 72
IPV6_MINHOPCOUNT = 73
IPV6_ORIGDSTADDR = 74
IPV6_RECVORIGDSTADDR = IPV6_ORIGDSTADDR
IPV6_TRANSPARENT = 75
IPV6_UNICAST_IF = 76
IPV6_RECVFRAGSIZE = 77
IPV6_FREEBIND = 78
)
+14
View File
@@ -108,3 +108,17 @@ const NetlinkAttrHeaderSize = 4
// NLA_ALIGNTO is the alignment of netlink attributes, from
// uapi/linux/netlink.h.
const NLA_ALIGNTO = 4
// Socket options, from uapi/linux/netlink.h.
const (
NETLINK_ADD_MEMBERSHIP = 1
NETLINK_DROP_MEMBERSHIP = 2
NETLINK_PKTINFO = 3
NETLINK_BROADCAST_ERROR = 4
NETLINK_NO_ENOBUFS = 5
NETLINK_LISTEN_ALL_NSID = 8
NETLINK_LIST_MEMBERSHIPS = 9
NETLINK_CAP_ACK = 10
NETLINK_EXT_ACK = 11
NETLINK_DUMP_STRICT_CHK = 12
)
+69 -18
View File
@@ -89,8 +89,18 @@ const (
MSG_CMSG_CLOEXEC = 0x40000000
)
// SOL_SOCKET is from socket.h
const SOL_SOCKET = 1
// Set/get socket option levels, from socket.h.
const (
SOL_IP = 0
SOL_SOCKET = 1
SOL_TCP = 6
SOL_UDP = 17
SOL_IPV6 = 41
SOL_ICMPV6 = 58
SOL_RAW = 255
SOL_PACKET = 263
SOL_NETLINK = 270
)
// Socket types, from linux/net.h.
const (
@@ -122,22 +132,63 @@ const (
// Socket options from socket.h.
const (
SO_ERROR = 4
SO_KEEPALIVE = 9
SO_LINGER = 13
SO_MARK = 36
SO_PASSCRED = 16
SO_PEERCRED = 17
SO_PEERNAME = 28
SO_PROTOCOL = 38
SO_RCVBUF = 8
SO_RCVTIMEO = 20
SO_REUSEADDR = 2
SO_SNDBUF = 7
SO_SNDTIMEO = 21
SO_TIMESTAMP = 29
SO_TIMESTAMPNS = 35
SO_TYPE = 3
SO_DEBUG = 1
SO_REUSEADDR = 2
SO_TYPE = 3
SO_ERROR = 4
SO_DONTROUTE = 5
SO_BROADCAST = 6
SO_SNDBUF = 7
SO_RCVBUF = 8
SO_KEEPALIVE = 9
SO_OOBINLINE = 10
SO_NO_CHECK = 11
SO_PRIORITY = 12
SO_LINGER = 13
SO_BSDCOMPAT = 14
SO_REUSEPORT = 15
SO_PASSCRED = 16
SO_PEERCRED = 17
SO_RCVLOWAT = 18
SO_SNDLOWAT = 19
SO_RCVTIMEO = 20
SO_SNDTIMEO = 21
SO_BINDTODEVICE = 25
SO_ATTACH_FILTER = 26
SO_DETACH_FILTER = 27
SO_GET_FILTER = SO_ATTACH_FILTER
SO_PEERNAME = 28
SO_TIMESTAMP = 29
SO_ACCEPTCONN = 30
SO_PEERSEC = 31
SO_SNDBUFFORCE = 32
SO_RCVBUFFORCE = 33
SO_PASSSEC = 34
SO_TIMESTAMPNS = 35
SO_MARK = 36
SO_TIMESTAMPING = 37
SO_PROTOCOL = 38
SO_DOMAIN = 39
SO_RXQ_OVFL = 40
SO_WIFI_STATUS = 41
SO_PEEK_OFF = 42
SO_NOFCS = 43
SO_LOCK_FILTER = 44
SO_SELECT_ERR_QUEUE = 45
SO_BUSY_POLL = 46
SO_MAX_PACING_RATE = 47
SO_BPF_EXTENSIONS = 48
SO_INCOMING_CPU = 49
SO_ATTACH_BPF = 50
SO_ATTACH_REUSEPORT_CBPF = 51
SO_ATTACH_REUSEPORT_EBPF = 52
SO_CNX_ADVICE = 53
SO_MEMINFO = 55
SO_INCOMING_NAPI_ID = 56
SO_COOKIE = 57
SO_PEERGROUPS = 59
SO_ZEROCOPY = 60
SO_TXTIME = 61
)
// SockAddrMax is the maximum size of a struct sockaddr, from
+54
View File
@@ -0,0 +1,54 @@
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package linux
// Socket options from uapi/linux/tcp.h.
const (
TCP_NODELAY = 1
TCP_MAXSEG = 2
TCP_CORK = 3
TCP_KEEPIDLE = 4
TCP_KEEPINTVL = 5
TCP_KEEPCNT = 6
TCP_SYNCNT = 7
TCP_LINGER2 = 8
TCP_DEFER_ACCEPT = 9
TCP_WINDOW_CLAMP = 10
TCP_INFO = 11
TCP_QUICKACK = 12
TCP_CONGESTION = 13
TCP_MD5SIG = 14
TCP_THIN_LINEAR_TIMEOUTS = 16
TCP_THIN_DUPACK = 17
TCP_USER_TIMEOUT = 18
TCP_REPAIR = 19
TCP_REPAIR_QUEUE = 20
TCP_QUEUE_SEQ = 21
TCP_REPAIR_OPTIONS = 22
TCP_FASTOPEN = 23
TCP_TIMESTAMP = 24
TCP_NOTSENT_LOWAT = 25
TCP_CC_INFO = 26
TCP_SAVE_SYN = 27
TCP_SAVED_SYN = 28
TCP_REPAIR_WINDOW = 29
TCP_FASTOPEN_CONNECT = 30
TCP_ULP = 31
TCP_MD5SIG_EXT = 32
TCP_FASTOPEN_KEY = 33
TCP_FASTOPEN_NO_COOKIE = 34
TCP_ZEROCOPY_RECEIVE = 35
TCP_INQ = 36
)
File diff suppressed because it is too large Load Diff
+33
View File
@@ -299,6 +299,21 @@ func (s *Socket) GetSockOpt(t *kernel.Task, level int, name int, outLen int) (in
}
// We don't have limit on receiving size.
return math.MaxInt32, nil
default:
socket.GetSockOptEmitUnimplementedEvent(t, name)
}
case linux.SOL_NETLINK:
switch name {
case linux.NETLINK_BROADCAST_ERROR,
linux.NETLINK_CAP_ACK,
linux.NETLINK_DUMP_STRICT_CHK,
linux.NETLINK_EXT_ACK,
linux.NETLINK_LIST_MEMBERSHIPS,
linux.NETLINK_NO_ENOBUFS,
linux.NETLINK_PKTINFO:
t.Kernel().EmitUnimplementedEvent(t)
}
}
// TODO: other sockopts are not supported.
@@ -329,7 +344,25 @@ func (s *Socket) SetSockOpt(t *kernel.Task, level int, name int, opt []byte) *sy
// We don't have limit on receiving size. So just accept anything as
// valid for compatibility.
return nil
default:
socket.SetSockOptEmitUnimplementedEvent(t, name)
}
case linux.SOL_NETLINK:
switch name {
case linux.NETLINK_ADD_MEMBERSHIP,
linux.NETLINK_BROADCAST_ERROR,
linux.NETLINK_CAP_ACK,
linux.NETLINK_DROP_MEMBERSHIP,
linux.NETLINK_DUMP_STRICT_CHK,
linux.NETLINK_EXT_ACK,
linux.NETLINK_LISTEN_ALL_NSID,
linux.NETLINK_NO_ENOBUFS,
linux.NETLINK_PKTINFO:
t.Kernel().EmitUnimplementedEvent(t)
}
}
// TODO: other sockopts are not supported.
return syserr.ErrProtocolNotAvailable
+91
View File
@@ -213,3 +213,94 @@ func (rt *ReceiveTimeout) SetRecvTimeout(nanoseconds int64) {
func (rt *ReceiveTimeout) RecvTimeout() int64 {
return atomic.LoadInt64(&rt.ns)
}
// GetSockOptEmitUnimplementedEvent emits unimplemented event if name is valid.
// It contains names that are valid for GetSockOpt when level is SOL_SOCKET.
func GetSockOptEmitUnimplementedEvent(t *kernel.Task, name int) {
switch name {
case linux.SO_ACCEPTCONN,
linux.SO_BPF_EXTENSIONS,
linux.SO_COOKIE,
linux.SO_DOMAIN,
linux.SO_ERROR,
linux.SO_GET_FILTER,
linux.SO_INCOMING_NAPI_ID,
linux.SO_MEMINFO,
linux.SO_PEERCRED,
linux.SO_PEERGROUPS,
linux.SO_PEERNAME,
linux.SO_PEERSEC,
linux.SO_PROTOCOL,
linux.SO_SNDLOWAT,
linux.SO_TYPE:
t.Kernel().EmitUnimplementedEvent(t)
default:
emitUnimplementedEvent(t, name)
}
}
// SetSockOptEmitUnimplementedEvent emits unimplemented event if name is valid.
// It contains names that are valid for SetSockOpt when level is SOL_SOCKET.
func SetSockOptEmitUnimplementedEvent(t *kernel.Task, name int) {
switch name {
case linux.SO_ATTACH_BPF,
linux.SO_ATTACH_FILTER,
linux.SO_ATTACH_REUSEPORT_CBPF,
linux.SO_ATTACH_REUSEPORT_EBPF,
linux.SO_CNX_ADVICE,
linux.SO_DETACH_FILTER,
linux.SO_RCVBUFFORCE,
linux.SO_SNDBUFFORCE:
t.Kernel().EmitUnimplementedEvent(t)
default:
emitUnimplementedEvent(t, name)
}
}
// emitUnimplementedEvent emits unimplemented event if name is valid. It
// contains names that are common between Get and SetSocketOpt when level is
// SOL_SOCKET.
func emitUnimplementedEvent(t *kernel.Task, name int) {
switch name {
case linux.SO_BINDTODEVICE,
linux.SO_BROADCAST,
linux.SO_BSDCOMPAT,
linux.SO_BUSY_POLL,
linux.SO_DEBUG,
linux.SO_DONTROUTE,
linux.SO_INCOMING_CPU,
linux.SO_KEEPALIVE,
linux.SO_LINGER,
linux.SO_LOCK_FILTER,
linux.SO_MARK,
linux.SO_MAX_PACING_RATE,
linux.SO_NOFCS,
linux.SO_NO_CHECK,
linux.SO_OOBINLINE,
linux.SO_PASSCRED,
linux.SO_PASSSEC,
linux.SO_PEEK_OFF,
linux.SO_PRIORITY,
linux.SO_RCVBUF,
linux.SO_RCVLOWAT,
linux.SO_RCVTIMEO,
linux.SO_REUSEADDR,
linux.SO_REUSEPORT,
linux.SO_RXQ_OVFL,
linux.SO_SELECT_ERR_QUEUE,
linux.SO_SNDBUF,
linux.SO_SNDTIMEO,
linux.SO_TIMESTAMP,
linux.SO_TIMESTAMPING,
linux.SO_TIMESTAMPNS,
linux.SO_TXTIME,
linux.SO_WIFI_STATUS,
linux.SO_ZEROCOPY:
t.Kernel().EmitUnimplementedEvent(t)
}
}
+8 -2
View File
@@ -89,10 +89,16 @@ func (c *compatEmitter) Emit(msg proto.Message) (hangup bool, err error) {
if tr == nil {
switch sysnr {
case syscall.SYS_PRCTL, syscall.SYS_ARCH_PRCTL:
tr = newCmdTracker(0)
// args: cmd, ...
tr = newArgsTracker(0)
case syscall.SYS_IOCTL, syscall.SYS_EPOLL_CTL, syscall.SYS_SHMCTL:
tr = newCmdTracker(1)
// args: fd, cmd, ...
tr = newArgsTracker(1)
case syscall.SYS_GETSOCKOPT, syscall.SYS_SETSOCKOPT:
// args: fd, level, name, ...
tr = newArgsTracker(1, 2)
default:
tr = &onceTracker{}
+39 -16
View File
@@ -20,35 +20,58 @@ import (
rpb "gvisor.googlesource.com/gvisor/pkg/sentry/arch/registers_go_proto"
)
// cmdTracker reports only a single time for each different command argument in
// the syscall. It's used for generic syscalls like ioctl to report once per
// 'cmd'
type cmdTracker struct {
// argIdx is the syscall argument index where the command is located.
argIdx int
cmds map[uint32]struct{}
// reportLimit is the max number of events that should be reported per tracker.
const reportLimit = 100
// argsTracker reports only once for each different combination of arguments.
// It's used for generic syscalls like ioctl to report once per 'cmd'.
type argsTracker struct {
// argsIdx is the syscall arguments to use as unique ID.
argsIdx []int
reported map[string]struct{}
count int
}
func newCmdTracker(argIdx int) *cmdTracker {
return &cmdTracker{argIdx: argIdx, cmds: make(map[uint32]struct{})}
func newArgsTracker(argIdx ...int) *argsTracker {
return &argsTracker{argsIdx: argIdx, reported: make(map[string]struct{})}
}
// cmd returns the command based on the syscall argument index.
func (c *cmdTracker) cmd(regs *rpb.AMD64Registers) uint32 {
switch c.argIdx {
func (a *argsTracker) key(regs *rpb.AMD64Registers) string {
var rv string
for _, idx := range a.argsIdx {
rv += fmt.Sprintf("%d|", argVal(idx, regs))
}
return rv
}
func argVal(argIdx int, regs *rpb.AMD64Registers) uint32 {
switch argIdx {
case 0:
return uint32(regs.Rdi)
case 1:
return uint32(regs.Rsi)
case 2:
return uint32(regs.Rdx)
case 3:
return uint32(regs.R10)
case 4:
return uint32(regs.R8)
case 5:
return uint32(regs.R9)
}
panic(fmt.Sprintf("unsupported syscall argument index %d", c.argIdx))
panic(fmt.Sprintf("invalid syscall argument index %d", argIdx))
}
func (c *cmdTracker) shouldReport(regs *rpb.AMD64Registers) bool {
_, ok := c.cmds[c.cmd(regs)]
func (a *argsTracker) shouldReport(regs *rpb.AMD64Registers) bool {
if a.count >= reportLimit {
return false
}
_, ok := a.reported[a.key(regs)]
return !ok
}
func (c *cmdTracker) onReported(regs *rpb.AMD64Registers) {
c.cmds[c.cmd(regs)] = struct{}{}
func (a *argsTracker) onReported(regs *rpb.AMD64Registers) {
a.count++
a.reported[a.key(regs)] = struct{}{}
}
+29 -10
View File
@@ -33,34 +33,53 @@ func TestOnceTracker(t *testing.T) {
}
}
func TestCmdTracker(t *testing.T) {
func TestArgsTracker(t *testing.T) {
for _, tc := range []struct {
name string
idx int
idx []int
rdi1 uint64
rdi2 uint64
rsi1 uint64
rsi2 uint64
want bool
}{
{name: "same rdi", idx: 0, rdi1: 123, rdi2: 123, want: false},
{name: "same rsi", idx: 1, rsi1: 123, rsi2: 123, want: false},
{name: "diff rdi", idx: 0, rdi1: 123, rdi2: 321, want: true},
{name: "diff rsi", idx: 1, rsi1: 123, rsi2: 321, want: true},
{name: "cmd is uint32", idx: 0, rsi1: 0xdead00000123, rsi2: 0xbeef00000123, want: false},
{name: "same rdi", idx: []int{0}, rdi1: 123, rdi2: 123, want: false},
{name: "same rsi", idx: []int{1}, rsi1: 123, rsi2: 123, want: false},
{name: "diff rdi", idx: []int{0}, rdi1: 123, rdi2: 321, want: true},
{name: "diff rsi", idx: []int{1}, rsi1: 123, rsi2: 321, want: true},
{name: "cmd is uint32", idx: []int{0}, rsi1: 0xdead00000123, rsi2: 0xbeef00000123, want: false},
{name: "same 2 args", idx: []int{0, 1}, rsi1: 123, rdi1: 321, rsi2: 123, rdi2: 321, want: false},
{name: "diff 2 args", idx: []int{0, 1}, rsi1: 123, rdi1: 321, rsi2: 789, rdi2: 987, want: true},
} {
t.Run(tc.name, func(t *testing.T) {
c := newCmdTracker(tc.idx)
c := newArgsTracker(tc.idx...)
regs := &rpb.AMD64Registers{Rdi: tc.rdi1, Rsi: tc.rsi1}
if !c.shouldReport(regs) {
t.Error("first call to checkAndMark, got: false, want: true")
t.Error("first call to shouldReport, got: false, want: true")
}
c.onReported(regs)
regs.Rdi, regs.Rsi = tc.rdi2, tc.rsi2
if got := c.shouldReport(regs); tc.want != got {
t.Errorf("after first call to checkAndMark, got: %t, want: %t", got, tc.want)
t.Errorf("second call to shouldReport, got: %t, want: %t", got, tc.want)
}
})
}
}
func TestArgsTrackerLimit(t *testing.T) {
c := newArgsTracker(0, 1)
for i := 0; i < reportLimit; i++ {
regs := &rpb.AMD64Registers{Rdi: 123, Rsi: uint64(i)}
if !c.shouldReport(regs) {
t.Error("shouldReport before limit was reached, got: false, want: true")
}
c.onReported(regs)
}
// Should hit the count limit now.
regs := &rpb.AMD64Registers{Rdi: 123, Rsi: 123456}
if c.shouldReport(regs) {
t.Error("shouldReport after limit was reached, got: true, want: false")
}
}