summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/strace/socket.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/sentry/strace/socket.go')
-rw-r--r--pkg/sentry/strace/socket.go674
1 files changed, 674 insertions, 0 deletions
diff --git a/pkg/sentry/strace/socket.go b/pkg/sentry/strace/socket.go
new file mode 100644
index 000000000..48c072e96
--- /dev/null
+++ b/pkg/sentry/strace/socket.go
@@ -0,0 +1,674 @@
+// Copyright 2018 Google Inc.
+//
+// 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 strace
+
+import (
+ "fmt"
+ "strings"
+
+ "gvisor.googlesource.com/gvisor/pkg/abi"
+ "gvisor.googlesource.com/gvisor/pkg/abi/linux"
+ "gvisor.googlesource.com/gvisor/pkg/binary"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/kernel"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/socket/control"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/socket/epsocket"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/socket/netlink"
+ slinux "gvisor.googlesource.com/gvisor/pkg/sentry/syscalls/linux"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
+)
+
+// SocketFamily are the possible socket(2) families.
+var SocketFamily = abi.ValueSet{
+ {
+ Value: linux.AF_UNSPEC,
+ Name: "AF_UNSPEC",
+ },
+ {
+ Value: linux.AF_UNIX,
+ Name: "AF_UNIX",
+ },
+ {
+ Value: linux.AF_INET,
+ Name: "AF_INET",
+ },
+ {
+ Value: linux.AF_AX25,
+ Name: "AF_AX25",
+ },
+ {
+ Value: linux.AF_IPX,
+ Name: "AF_IPX",
+ },
+ {
+ Value: linux.AF_APPLETALK,
+ Name: "AF_APPLETALK",
+ },
+ {
+ Value: linux.AF_NETROM,
+ Name: "AF_NETROM",
+ },
+ {
+ Value: linux.AF_BRIDGE,
+ Name: "AF_BRIDGE",
+ },
+ {
+ Value: linux.AF_ATMPVC,
+ Name: "AF_ATMPVC",
+ },
+ {
+ Value: linux.AF_X25,
+ Name: "AF_X25",
+ },
+ {
+ Value: linux.AF_INET6,
+ Name: "AF_INET6",
+ },
+ {
+ Value: linux.AF_ROSE,
+ Name: "AF_ROSE",
+ },
+ {
+ Value: linux.AF_DECnet,
+ Name: "AF_DECnet",
+ },
+ {
+ Value: linux.AF_NETBEUI,
+ Name: "AF_NETBEUI",
+ },
+ {
+ Value: linux.AF_SECURITY,
+ Name: "AF_SECURITY",
+ },
+ {
+ Value: linux.AF_KEY,
+ Name: "AF_KEY",
+ },
+ {
+ Value: linux.AF_NETLINK,
+ Name: "AF_NETLINK",
+ },
+ {
+ Value: linux.AF_PACKET,
+ Name: "AF_PACKET",
+ },
+ {
+ Value: linux.AF_ASH,
+ Name: "AF_ASH",
+ },
+ {
+ Value: linux.AF_ECONET,
+ Name: "AF_ECONET",
+ },
+ {
+ Value: linux.AF_ATMSVC,
+ Name: "AF_ATMSVC",
+ },
+ {
+ Value: linux.AF_RDS,
+ Name: "AF_RDS",
+ },
+ {
+ Value: linux.AF_SNA,
+ Name: "AF_SNA",
+ },
+ {
+ Value: linux.AF_IRDA,
+ Name: "AF_IRDA",
+ },
+ {
+ Value: linux.AF_PPPOX,
+ Name: "AF_PPPOX",
+ },
+ {
+ Value: linux.AF_WANPIPE,
+ Name: "AF_WANPIPE",
+ },
+ {
+ Value: linux.AF_LLC,
+ Name: "AF_LLC",
+ },
+ {
+ Value: linux.AF_IB,
+ Name: "AF_IB",
+ },
+ {
+ Value: linux.AF_MPLS,
+ Name: "AF_MPLS",
+ },
+ {
+ Value: linux.AF_CAN,
+ Name: "AF_CAN",
+ },
+ {
+ Value: linux.AF_TIPC,
+ Name: "AF_TIPC",
+ },
+ {
+ Value: linux.AF_BLUETOOTH,
+ Name: "AF_BLUETOOTH",
+ },
+ {
+ Value: linux.AF_IUCV,
+ Name: "AF_IUCV",
+ },
+ {
+ Value: linux.AF_RXRPC,
+ Name: "AF_RXRPC",
+ },
+ {
+ Value: linux.AF_ISDN,
+ Name: "AF_ISDN",
+ },
+ {
+ Value: linux.AF_PHONET,
+ Name: "AF_PHONET",
+ },
+ {
+ Value: linux.AF_IEEE802154,
+ Name: "AF_IEEE802154",
+ },
+ {
+ Value: linux.AF_CAIF,
+ Name: "AF_CAIF",
+ },
+ {
+ Value: linux.AF_ALG,
+ Name: "AF_ALG",
+ },
+ {
+ Value: linux.AF_NFC,
+ Name: "AF_NFC",
+ },
+ {
+ Value: linux.AF_VSOCK,
+ Name: "AF_VSOCK",
+ },
+}
+
+// SocketType are the possible socket(2) types.
+var SocketType = abi.ValueSet{
+ {
+ Value: linux.SOCK_STREAM,
+ Name: "SOCK_STREAM",
+ },
+ {
+ Value: linux.SOCK_DGRAM,
+ Name: "SOCK_DGRAM",
+ },
+ {
+ Value: linux.SOCK_RAW,
+ Name: "SOCK_RAW",
+ },
+ {
+ Value: linux.SOCK_RDM,
+ Name: "SOCK_RDM",
+ },
+ {
+ Value: linux.SOCK_SEQPACKET,
+ Name: "SOCK_SEQPACKET",
+ },
+ {
+ Value: linux.SOCK_DCCP,
+ Name: "SOCK_DCCP",
+ },
+ {
+ Value: linux.SOCK_PACKET,
+ Name: "SOCK_PACKET",
+ },
+}
+
+// SocketFlagSet are the possible socket(2) flags.
+var SocketFlagSet = abi.FlagSet{
+ {
+ Flag: linux.SOCK_CLOEXEC,
+ Name: "SOCK_CLOEXEC",
+ },
+ {
+ Flag: linux.SOCK_NONBLOCK,
+ Name: "SOCK_NONBLOCK",
+ },
+}
+
+// ipProtocol are the possible socket(2) types for INET and INET6 sockets.
+var ipProtocol = abi.ValueSet{
+ {
+ Value: linux.IPPROTO_IP,
+ Name: "IPPROTO_IP",
+ },
+ {
+ Value: linux.IPPROTO_ICMP,
+ Name: "IPPROTO_ICMP",
+ },
+ {
+ Value: linux.IPPROTO_IGMP,
+ Name: "IPPROTO_IGMP",
+ },
+ {
+ Value: linux.IPPROTO_IPIP,
+ Name: "IPPROTO_IPIP",
+ },
+ {
+ Value: linux.IPPROTO_TCP,
+ Name: "IPPROTO_TCP",
+ },
+ {
+ Value: linux.IPPROTO_EGP,
+ Name: "IPPROTO_EGP",
+ },
+ {
+ Value: linux.IPPROTO_PUP,
+ Name: "IPPROTO_PUP",
+ },
+ {
+ Value: linux.IPPROTO_UDP,
+ Name: "IPPROTO_UDP",
+ },
+ {
+ Value: linux.IPPROTO_IDP,
+ Name: "IPPROTO_IDP",
+ },
+ {
+ Value: linux.IPPROTO_TP,
+ Name: "IPPROTO_TP",
+ },
+ {
+ Value: linux.IPPROTO_DCCP,
+ Name: "IPPROTO_DCCP",
+ },
+ {
+ Value: linux.IPPROTO_IPV6,
+ Name: "IPPROTO_IPV6",
+ },
+ {
+ Value: linux.IPPROTO_RSVP,
+ Name: "IPPROTO_RSVP",
+ },
+ {
+ Value: linux.IPPROTO_GRE,
+ Name: "IPPROTO_GRE",
+ },
+ {
+ Value: linux.IPPROTO_ESP,
+ Name: "IPPROTO_ESP",
+ },
+ {
+ Value: linux.IPPROTO_AH,
+ Name: "IPPROTO_AH",
+ },
+ {
+ Value: linux.IPPROTO_MTP,
+ Name: "IPPROTO_MTP",
+ },
+ {
+ Value: linux.IPPROTO_BEETPH,
+ Name: "IPPROTO_BEETPH",
+ },
+ {
+ Value: linux.IPPROTO_ENCAP,
+ Name: "IPPROTO_ENCAP",
+ },
+ {
+ Value: linux.IPPROTO_PIM,
+ Name: "IPPROTO_PIM",
+ },
+ {
+ Value: linux.IPPROTO_COMP,
+ Name: "IPPROTO_COMP",
+ },
+ {
+ Value: linux.IPPROTO_SCTP,
+ Name: "IPPROTO_SCTP",
+ },
+ {
+ Value: linux.IPPROTO_UDPLITE,
+ Name: "IPPROTO_UDPLITE",
+ },
+ {
+ Value: linux.IPPROTO_MPLS,
+ Name: "IPPROTO_MPLS",
+ },
+ {
+ Value: linux.IPPROTO_RAW,
+ Name: "IPPROTO_RAW",
+ },
+}
+
+// SocketProtocol are the possible socket(2) protocols for each protocol family.
+var SocketProtocol = map[int32]abi.ValueSet{
+ linux.AF_INET: ipProtocol,
+ linux.AF_INET6: ipProtocol,
+ linux.AF_NETLINK: {
+ {
+ Value: linux.NETLINK_ROUTE,
+ Name: "NETLINK_ROUTE",
+ },
+ {
+ Value: linux.NETLINK_UNUSED,
+ Name: "NETLINK_UNUSED",
+ },
+ {
+ Value: linux.NETLINK_USERSOCK,
+ Name: "NETLINK_USERSOCK",
+ },
+ {
+ Value: linux.NETLINK_FIREWALL,
+ Name: "NETLINK_FIREWALL",
+ },
+ {
+ Value: linux.NETLINK_SOCK_DIAG,
+ Name: "NETLINK_SOCK_DIAG",
+ },
+ {
+ Value: linux.NETLINK_NFLOG,
+ Name: "NETLINK_NFLOG",
+ },
+ {
+ Value: linux.NETLINK_XFRM,
+ Name: "NETLINK_XFRM",
+ },
+ {
+ Value: linux.NETLINK_SELINUX,
+ Name: "NETLINK_SELINUX",
+ },
+ {
+ Value: linux.NETLINK_ISCSI,
+ Name: "NETLINK_ISCSI",
+ },
+ {
+ Value: linux.NETLINK_AUDIT,
+ Name: "NETLINK_AUDIT",
+ },
+ {
+ Value: linux.NETLINK_FIB_LOOKUP,
+ Name: "NETLINK_FIB_LOOKUP",
+ },
+ {
+ Value: linux.NETLINK_CONNECTOR,
+ Name: "NETLINK_CONNECTOR",
+ },
+ {
+ Value: linux.NETLINK_NETFILTER,
+ Name: "NETLINK_NETFILTER",
+ },
+ {
+ Value: linux.NETLINK_IP6_FW,
+ Name: "NETLINK_IP6_FW",
+ },
+ {
+ Value: linux.NETLINK_DNRTMSG,
+ Name: "NETLINK_DNRTMSG",
+ },
+ {
+ Value: linux.NETLINK_KOBJECT_UEVENT,
+ Name: "NETLINK_KOBJECT_UEVENT",
+ },
+ {
+ Value: linux.NETLINK_GENERIC,
+ Name: "NETLINK_GENERIC",
+ },
+ {
+ Value: linux.NETLINK_SCSITRANSPORT,
+ Name: "NETLINK_SCSITRANSPORT",
+ },
+ {
+ Value: linux.NETLINK_ECRYPTFS,
+ Name: "NETLINK_ECRYPTFS",
+ },
+ {
+ Value: linux.NETLINK_RDMA,
+ Name: "NETLINK_RDMA",
+ },
+ {
+ Value: linux.NETLINK_CRYPTO,
+ Name: "NETLINK_CRYPTO",
+ },
+ },
+}
+
+var controlMessageType = map[int32]string{
+ linux.SCM_RIGHTS: "SCM_RIGHTS",
+ linux.SCM_CREDENTIALS: "SCM_CREDENTIALS",
+}
+
+func cmsghdr(t *kernel.Task, addr usermem.Addr, length uint64, maxBytes uint64) string {
+ if length > maxBytes {
+ return fmt.Sprintf("%#x (error decoding control: invalid length (%d))", addr, length)
+ }
+
+ buf := make([]byte, length)
+ if _, err := t.CopyIn(addr, &buf); err != nil {
+ return fmt.Sprintf("%#x (error decoding control: %v)", addr, err)
+ }
+
+ var strs []string
+
+ for i := 0; i < len(buf); {
+ if i+linux.SizeOfControlMessageHeader > len(buf) {
+ strs = append(strs, "{invalid control message (too short)}")
+ break
+ }
+
+ var h linux.ControlMessageHeader
+ binary.Unmarshal(buf[i:i+linux.SizeOfControlMessageHeader], usermem.ByteOrder, &h)
+ i += linux.SizeOfControlMessageHeader
+
+ var skipData bool
+ level := "SOL_SOCKET"
+ if h.Level != linux.SOL_SOCKET {
+ skipData = true
+ level = fmt.Sprint(h.Level)
+ }
+
+ typ, ok := controlMessageType[h.Type]
+ if !ok {
+ skipData = true
+ typ = fmt.Sprint(h.Type)
+ }
+
+ if h.Length > uint64(len(buf)-i) {
+ strs = append(strs, fmt.Sprintf(
+ "{level=%s, type=%s, length=%d, content extends beyond buffer}",
+ level,
+ typ,
+ h.Length,
+ ))
+ break
+ }
+
+ width := t.Arch().Width()
+ length := int(h.Length) - linux.SizeOfControlMessageHeader
+
+ if skipData {
+ strs = append(strs, fmt.Sprintf("{level=%s, type=%s, length=%d}", level, typ, h.Length))
+ i += control.AlignUp(i+length, width)
+ continue
+ }
+
+ switch h.Type {
+ case linux.SCM_RIGHTS:
+ rightsSize := control.AlignDown(length, linux.SizeOfControlMessageRight)
+
+ numRights := rightsSize / linux.SizeOfControlMessageRight
+ fds := make(linux.ControlMessageRights, numRights)
+ binary.Unmarshal(buf[i:i+rightsSize], usermem.ByteOrder, &fds)
+
+ rights := make([]string, 0, len(fds))
+ for _, fd := range fds {
+ rights = append(rights, fmt.Sprint(fd))
+ }
+
+ strs = append(strs, fmt.Sprintf(
+ "{level=%s, type=%s, length=%d, content: %s}",
+ level,
+ typ,
+ h.Length,
+ strings.Join(rights, ","),
+ ))
+
+ i += control.AlignUp(length, width)
+
+ case linux.SCM_CREDENTIALS:
+ if length < linux.SizeOfControlMessageCredentials {
+ strs = append(strs, fmt.Sprintf(
+ "{level=%s, type=%s, length=%d, content too short}",
+ level,
+ typ,
+ h.Length,
+ ))
+ i += control.AlignUp(length, width)
+ break
+ }
+
+ var creds linux.ControlMessageCredentials
+ binary.Unmarshal(buf[i:i+linux.SizeOfControlMessageCredentials], usermem.ByteOrder, &creds)
+
+ strs = append(strs, fmt.Sprintf(
+ "{level=%s, type=%s, length=%d, pid: %d, uid: %d, gid: %d}",
+ level,
+ typ,
+ h.Length,
+ creds.PID,
+ creds.UID,
+ creds.GID,
+ ))
+
+ i += control.AlignUp(length, width)
+
+ default:
+ panic("unreachable")
+ }
+ }
+
+ return fmt.Sprintf("%#x %s", addr, strings.Join(strs, ", "))
+}
+
+func msghdr(t *kernel.Task, addr usermem.Addr, printContent bool, maxBytes uint64) string {
+ var msg slinux.MessageHeader64
+ if err := slinux.CopyInMessageHeader64(t, addr, &msg); err != nil {
+ return fmt.Sprintf("%#x (error decoding msghdr: %v)", addr, err)
+ }
+ s := fmt.Sprintf(
+ "%#x {name=%#x, namelen=%d, iovecs=%s",
+ addr,
+ msg.Name,
+ msg.NameLen,
+ iovecs(t, usermem.Addr(msg.Iov), int(msg.IovLen), printContent, maxBytes),
+ )
+ if printContent {
+ s = fmt.Sprintf("%s, control={%s}", s, cmsghdr(t, usermem.Addr(msg.Control), msg.ControlLen, maxBytes))
+ } else {
+ s = fmt.Sprintf("%s, control=%#x, control_len=%d", s, msg.Control, msg.ControlLen)
+ }
+ return fmt.Sprintf("%s, flags=%d}", s, msg.Flags)
+}
+
+func sockAddr(t *kernel.Task, addr usermem.Addr, length uint32) string {
+ if addr == 0 {
+ return "null"
+ }
+
+ b, err := slinux.CaptureAddress(t, addr, length)
+ if err != nil {
+ return fmt.Sprintf("%#x {error reading address: %v}", addr, err)
+ }
+
+ // Extract address family.
+ if len(b) < 2 {
+ return fmt.Sprintf("%#x {address too short: %d bytes}", addr, len(b))
+ }
+ family := usermem.ByteOrder.Uint16(b)
+
+ familyStr := SocketFamily.Parse(uint64(family))
+
+ switch family {
+ case linux.AF_INET, linux.AF_INET6, linux.AF_UNIX:
+ fa, err := epsocket.GetAddress(int(family), b)
+ if err != nil {
+ return fmt.Sprintf("%#x {Family: %s, error extracting address: %v}", addr, familyStr, err)
+ }
+
+ if family == linux.AF_UNIX {
+ return fmt.Sprintf("%#x {Family: %s, Addr: %q}", addr, familyStr, string(fa.Addr))
+ }
+
+ return fmt.Sprintf("%#x {Family: %s, Addr: %v, Port: %d}", addr, familyStr, fa.Addr, fa.Port)
+ case linux.AF_NETLINK:
+ sa, err := netlink.ExtractSockAddr(b)
+ if err != nil {
+ return fmt.Sprintf("%#x {Family: %s, error extracting address: %v}", addr, familyStr, err)
+ }
+ return fmt.Sprintf("%#x {Family: %s, PortID: %d, Groups: %d}", addr, familyStr, sa.PortID, sa.Groups)
+ default:
+ return fmt.Sprintf("%#x {Family: %s, family addr format unknown}", addr, familyStr)
+ }
+}
+
+func postSockAddr(t *kernel.Task, addr usermem.Addr, lengthPtr usermem.Addr) string {
+ if addr == 0 {
+ return "null"
+ }
+
+ if lengthPtr == 0 {
+ return fmt.Sprintf("%#x {length null}", addr)
+ }
+
+ l, err := copySockLen(t, lengthPtr)
+ if err != nil {
+ return fmt.Sprintf("%#x {error reading length: %v}", addr, err)
+ }
+
+ return sockAddr(t, addr, l)
+}
+
+func copySockLen(t *kernel.Task, addr usermem.Addr) (uint32, error) {
+ // socklen_t is 32-bits.
+ var l uint32
+ _, err := t.CopyIn(addr, &l)
+ return l, err
+}
+
+func sockLenPointer(t *kernel.Task, addr usermem.Addr) string {
+ if addr == 0 {
+ return "null"
+ }
+ l, err := copySockLen(t, addr)
+ if err != nil {
+ return fmt.Sprintf("%#x {error reading length: %v}", addr, err)
+ }
+ return fmt.Sprintf("%#x {length=%v}", addr, l)
+}
+
+func sockType(stype int32) string {
+ s := SocketType.Parse(uint64(stype & linux.SOCK_TYPE_MASK))
+ if flags := SocketFlagSet.Parse(uint64(stype &^ linux.SOCK_TYPE_MASK)); flags != "" {
+ s += "|" + flags
+ }
+ return s
+}
+
+func sockProtocol(family, protocol int32) string {
+ protocols, ok := SocketProtocol[family]
+ if !ok {
+ return fmt.Sprintf("%#x", protocol)
+ }
+ return protocols.Parse(uint64(protocol))
+}
+
+func sockFlags(flags int32) string {
+ if flags == 0 {
+ return "0"
+ }
+ return SocketFlagSet.Parse(uint64(flags))
+}