diff options
-rw-r--r-- | .github/workflows/stale.yml | 2 | ||||
-rw-r--r-- | README.md | 3 | ||||
-rw-r--r-- | WORKSPACE | 14 | ||||
-rw-r--r-- | pkg/crypto/crypto_stdlib.go | 8 | ||||
-rw-r--r-- | pkg/linuxerr/BUILD | 20 | ||||
-rw-r--r-- | pkg/linuxerr/linuxerr.go | 184 | ||||
-rw-r--r-- | pkg/linuxerr/linuxerr_test.go (renamed from pkg/syserror/syserror_test.go) | 42 | ||||
-rw-r--r-- | pkg/sentry/fs/gofer/file.go | 1 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/gofer/regular_file.go | 1 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/gofer/special_file.go | 1 | ||||
-rw-r--r-- | pkg/sentry/fsmetric/fsmetric.go | 1 | ||||
-rw-r--r-- | pkg/shim/BUILD | 14 | ||||
-rw-r--r-- | pkg/shim/service.go | 50 | ||||
-rw-r--r-- | pkg/shim/service_test.go | 121 | ||||
-rw-r--r-- | pkg/shim/utils/annotations.go | 6 | ||||
-rw-r--r-- | pkg/shim/utils/utils.go | 20 | ||||
-rw-r--r-- | pkg/shim/utils/volumes.go | 20 | ||||
-rw-r--r-- | pkg/shim/utils/volumes_test.go | 56 | ||||
-rw-r--r-- | pkg/syserror/BUILD | 11 | ||||
-rw-r--r-- | pkg/tcpip/stack/packet_buffer.go | 4 | ||||
-rw-r--r-- | test/syscalls/linux/BUILD | 1 | ||||
-rw-r--r-- | test/syscalls/linux/pty.cc | 24 | ||||
-rw-r--r-- | test/syscalls/linux/socket_ip_tcp_generic.cc | 2 |
23 files changed, 507 insertions, 99 deletions
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index ef13f019f..ce300869c 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -1,6 +1,6 @@ # The stale workflow closes stale issues and pull requests, unless specific # tags have been applied in order to keep them open. -name: "Close stale issues" +name: "Stale issues" "on": schedule: - cron: "0 0 * * *" @@ -4,6 +4,9 @@ [![gVisor chat](https://badges.gitter.im/gvisor/community.png)](https://gitter.im/gvisor/community) [![code search](https://img.shields.io/badge/code-search-blue)](https://cs.opensource.google/gvisor/gvisor) +[![Issue reviver](https://github.com/google/gvisor/actions/workflows/issue_reviver.yml/badge.svg)](https://github.com/google/gvisor/actions/workflows/issue_reviver.yml) +[![Stale issues](https://github.com/google/gvisor/actions/workflows/stale.yml/badge.svg)](https://github.com/google/gvisor/actions/workflows/stale.yml) + ## What is gVisor? **gVisor** is an application kernel, written in Go, that implements a @@ -42,10 +42,10 @@ http_archive( # binaries of symbols, which we don't want. "//tools:rules_go_symbols.patch", ], - sha256 = "7904dbecbaffd068651916dce77ff3437679f9d20e1a7956bff43826e7645fcc", + sha256 = "69de5c704a05ff37862f7e0f5534d4f479418afc21806c887db544a316f3cb6b", urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.25.1/rules_go-v0.25.1.tar.gz", - "https://github.com/bazelbuild/rules_go/releases/download/v0.25.1/rules_go-v0.25.1.tar.gz", + "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.27.0/rules_go-v0.27.0.tar.gz", + "https://github.com/bazelbuild/rules_go/releases/download/v0.27.0/rules_go-v0.27.0.tar.gz", ], ) @@ -58,10 +58,10 @@ http_archive( # slightly future proof this mechanism. "//tools:bazel_gazelle_generate.patch", ], - sha256 = "222e49f034ca7a1d1231422cdb67066b885819885c356673cb1f72f748a3c9d4", + sha256 = "62ca106be173579c0a167deb23358fdfe71ffa1e4cfdddf5582af26520f1c66f", urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.22.3/bazel-gazelle-v0.22.3.tar.gz", - "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.22.3/bazel-gazelle-v0.22.3.tar.gz", + "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.23.0/bazel-gazelle-v0.23.0.tar.gz", + "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.23.0/bazel-gazelle-v0.23.0.tar.gz", ], ) @@ -69,7 +69,7 @@ load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_depe go_rules_dependencies() -go_register_toolchains(go_version = "1.15.7") +go_register_toolchains(go_version = "1.16.2") load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository") diff --git a/pkg/crypto/crypto_stdlib.go b/pkg/crypto/crypto_stdlib.go index 514592b08..74a55a123 100644 --- a/pkg/crypto/crypto_stdlib.go +++ b/pkg/crypto/crypto_stdlib.go @@ -22,11 +22,11 @@ import ( // EcdsaVerify verifies the signature in r, s of hash using ECDSA and the // public key, pub. Its return value records whether the signature is valid. -func EcdsaVerify(pub *ecdsa.PublicKey, hash []byte, r, s *big.Int) (bool, error) { - return ecdsa.Verify(pub, hash, r, s), nil +func EcdsaVerify(pub *ecdsa.PublicKey, hash []byte, r, s *big.Int) bool { + return ecdsa.Verify(pub, hash, r, s) } // SumSha384 returns the SHA384 checksum of the data. -func SumSha384(data []byte) ([sha512.Size384]byte, error) { - return sha512.Sum384(data), nil +func SumSha384(data []byte) (sum384 [sha512.Size384]byte) { + return sha512.Sum384(data) } diff --git a/pkg/linuxerr/BUILD b/pkg/linuxerr/BUILD new file mode 100644 index 000000000..c5abbd34f --- /dev/null +++ b/pkg/linuxerr/BUILD @@ -0,0 +1,20 @@ +load("//tools:defs.bzl", "go_library", "go_test") + +package(licenses = ["notice"]) + +go_library( + name = "linuxerr", + srcs = ["linuxerr.go"], + visibility = ["//visibility:public"], + deps = ["//pkg/abi/linux"], +) + +go_test( + name = "linuxerr_test", + srcs = ["linuxerr_test.go"], + deps = [ + ":linuxerr", + "//pkg/syserror", + "@org_golang_x_sys//unix:go_default_library", + ], +) diff --git a/pkg/linuxerr/linuxerr.go b/pkg/linuxerr/linuxerr.go new file mode 100644 index 000000000..f45caaadf --- /dev/null +++ b/pkg/linuxerr/linuxerr.go @@ -0,0 +1,184 @@ +// Copyright 2021 The gVisor Authors. +// +// 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 linuxerr contains syscall error codes exported as an error interface +// pointers. This allows for fast comparison and return operations comperable +// to unix.Errno constants. +package linuxerr + +import ( + "gvisor.dev/gvisor/pkg/abi/linux" +) + +// Error represents a syscall errno with a descriptive message. +type Error struct { + errno linux.Errno + message string +} + +func new(err linux.Errno, message string) *Error { + return &Error{ + errno: err, + message: message, + } +} + +// Error implements error.Error. +func (e *Error) Error() string { return e.message } + +// Errno returns the underlying linux.Errno value. +func (e *Error) Errno() linux.Errno { return e.errno } + +// The following varables have the same meaning as their errno equivalent. + +// Errno values from include/uapi/asm-generic/errno-base.h. +var ( + EPERM = new(linux.EPERM, "operation not permitted") + ENOENT = new(linux.ENOENT, "no such file or directory") + ESRCH = new(linux.ESRCH, "no such process") + EINTR = new(linux.EINTR, "interrupted system call") + EIO = new(linux.EIO, "I/O error") + ENXIO = new(linux.ENXIO, "no such device or address") + E2BIG = new(linux.E2BIG, "argument list too long") + ENOEXEC = new(linux.ENOEXEC, "exec format error") + EBADF = new(linux.EBADF, "bad file number") + ECHILD = new(linux.ECHILD, "no child processes") + EAGAIN = new(linux.EAGAIN, "try again") + ENOMEM = new(linux.ENOMEM, "out of memory") + EACCES = new(linux.EACCES, "permission denied") + EFAULT = new(linux.EFAULT, "bad address") + ENOTBLK = new(linux.ENOTBLK, "block device required") + EBUSY = new(linux.EBUSY, "device or resource busy") + EEXIST = new(linux.EEXIST, "file exists") + EXDEV = new(linux.EXDEV, "cross-device link") + ENODEV = new(linux.ENODEV, "no such device") + ENOTDIR = new(linux.ENOTDIR, "not a directory") + EISDIR = new(linux.EISDIR, "is a directory") + EINVAL = new(linux.EINVAL, "invalid argument") + ENFILE = new(linux.ENFILE, "file table overflow") + EMFILE = new(linux.EMFILE, "too many open files") + ENOTTY = new(linux.ENOTTY, "not a typewriter") + ETXTBSY = new(linux.ETXTBSY, "text file busy") + EFBIG = new(linux.EFBIG, "file too large") + ENOSPC = new(linux.ENOSPC, "no space left on device") + ESPIPE = new(linux.ESPIPE, "illegal seek") + EROFS = new(linux.EROFS, "read-only file system") + EMLINK = new(linux.EMLINK, "too many links") + EPIPE = new(linux.EPIPE, "broken pipe") + EDOM = new(linux.EDOM, "math argument out of domain of func") + ERANGE = new(linux.ERANGE, "math result not representable") +) + +// Errno values from include/uapi/asm-generic/errno.h. +var ( + EDEADLK = new(linux.EDEADLK, "resource deadlock would occur") + ENAMETOOLONG = new(linux.ENAMETOOLONG, "file name too long") + ENOLCK = new(linux.ENOLCK, "no record locks available") + ENOSYS = new(linux.ENOSYS, "invalid system call number") + ENOTEMPTY = new(linux.ENOTEMPTY, "directory not empty") + ELOOP = new(linux.ELOOP, "too many symbolic links encountered") + EWOULDBLOCK = new(linux.EWOULDBLOCK, "operation would block") + ENOMSG = new(linux.ENOMSG, "no message of desired type") + EIDRM = new(linux.EIDRM, "identifier removed") + ECHRNG = new(linux.ECHRNG, "channel number out of range") + EL2NSYNC = new(linux.EL2NSYNC, "level 2 not synchronized") + EL3HLT = new(linux.EL3HLT, "level 3 halted") + EL3RST = new(linux.EL3RST, "level 3 reset") + ELNRNG = new(linux.ELNRNG, "link number out of range") + EUNATCH = new(linux.EUNATCH, "protocol driver not attached") + ENOCSI = new(linux.ENOCSI, "no CSI structure available") + EL2HLT = new(linux.EL2HLT, "level 2 halted") + EBADE = new(linux.EBADE, "invalid exchange") + EBADR = new(linux.EBADR, "invalid request descriptor") + EXFULL = new(linux.EXFULL, "exchange full") + ENOANO = new(linux.ENOANO, "no anode") + EBADRQC = new(linux.EBADRQC, "invalid request code") + EBADSLT = new(linux.EBADSLT, "invalid slot") + EDEADLOCK = new(linux.EDEADLOCK, EDEADLK.message) + EBFONT = new(linux.EBFONT, "bad font file format") + ENOSTR = new(linux.ENOSTR, "device not a stream") + ENODATA = new(linux.ENODATA, "no data available") + ETIME = new(linux.ETIME, "timer expired") + ENOSR = new(linux.ENOSR, "out of streams resources") + ENONET = new(linux.ENOENT, "machine is not on the network") + ENOPKG = new(linux.ENOPKG, "package not installed") + EREMOTE = new(linux.EREMOTE, "object is remote") + ENOLINK = new(linux.ENOLINK, "link has been severed") + EADV = new(linux.EADV, "advertise error") + ESRMNT = new(linux.ESRMNT, "srmount error") + ECOMM = new(linux.ECOMM, "communication error on send") + EPROTO = new(linux.EPROTO, "protocol error") + EMULTIHOP = new(linux.EMULTIHOP, "multihop attempted") + EDOTDOT = new(linux.EDOTDOT, "RFS specific error") + EBADMSG = new(linux.EBADMSG, "not a data message") + EOVERFLOW = new(linux.EOVERFLOW, "value too large for defined data type") + ENOTUNIQ = new(linux.ENOTUNIQ, "name not unique on network") + EBADFD = new(linux.EBADFD, "file descriptor in bad state") + EREMCHG = new(linux.EREMCHG, "remote address changed") + ELIBACC = new(linux.ELIBACC, "can not access a needed shared library") + ELIBBAD = new(linux.ELIBBAD, "accessing a corrupted shared library") + ELIBSCN = new(linux.ELIBSCN, ".lib section in a.out corrupted") + ELIBMAX = new(linux.ELIBMAX, "attempting to link in too many shared libraries") + ELIBEXEC = new(linux.ELIBEXEC, "cannot exec a shared library directly") + EILSEQ = new(linux.EILSEQ, "illegal byte sequence") + ERESTART = new(linux.ERESTART, "interrupted system call should be restarted") + ESTRPIPE = new(linux.ESTRPIPE, "streams pipe error") + EUSERS = new(linux.EUSERS, "too many users") + ENOTSOCK = new(linux.ENOTSOCK, "socket operation on non-socket") + EDESTADDRREQ = new(linux.EDESTADDRREQ, "destination address required") + EMSGSIZE = new(linux.EMSGSIZE, "message too long") + EPROTOTYPE = new(linux.EPROTOTYPE, "protocol wrong type for socket") + ENOPROTOOPT = new(linux.ENOPROTOOPT, "protocol not available") + EPROTONOSUPPORT = new(linux.EPROTONOSUPPORT, "protocol not supported") + ESOCKTNOSUPPORT = new(linux.ESOCKTNOSUPPORT, "socket type not supported") + EOPNOTSUPP = new(linux.EOPNOTSUPP, "operation not supported on transport endpoint") + EPFNOSUPPORT = new(linux.EPFNOSUPPORT, "protocol family not supported") + EAFNOSUPPORT = new(linux.EAFNOSUPPORT, "address family not supported by protocol") + EADDRINUSE = new(linux.EADDRINUSE, "address already in use") + EADDRNOTAVAIL = new(linux.EADDRNOTAVAIL, "cannot assign requested address") + ENETDOWN = new(linux.ENETDOWN, "network is down") + ENETUNREACH = new(linux.ENETUNREACH, "network is unreachable") + ENETRESET = new(linux.ENETRESET, "network dropped connection because of reset") + ECONNABORTED = new(linux.ECONNABORTED, "software caused connection abort") + ECONNRESET = new(linux.ECONNRESET, "connection reset by peer") + ENOBUFS = new(linux.ENOBUFS, "no buffer space available") + EISCONN = new(linux.EISCONN, "transport endpoint is already connected") + ENOTCONN = new(linux.ENOTCONN, "transport endpoint is not connected") + ESHUTDOWN = new(linux.ESHUTDOWN, "cannot send after transport endpoint shutdown") + ETOOMANYREFS = new(linux.ETOOMANYREFS, "too many references: cannot splice") + ETIMEDOUT = new(linux.ETIMEDOUT, "connection timed out") + ECONNREFUSED = new(linux.ECONNREFUSED, "connection refused") + EHOSTDOWN = new(linux.EHOSTDOWN, "host is down") + EHOSTUNREACH = new(linux.EHOSTUNREACH, "no route to host") + EALREADY = new(linux.EALREADY, "operation already in progress") + EINPROGRESS = new(linux.EINPROGRESS, "operation now in progress") + ESTALE = new(linux.ESTALE, "stale file handle") + EUCLEAN = new(linux.EUCLEAN, "structure needs cleaning") + ENOTNAM = new(linux.ENOTNAM, "not a XENIX named type file") + ENAVAIL = new(linux.ENAVAIL, "no XENIX semaphores available") + EISNAM = new(linux.EISNAM, "is a named type file") + EREMOTEIO = new(linux.EREMOTEIO, "remote I/O error") + EDQUOT = new(linux.EDQUOT, "quota exceeded") + ENOMEDIUM = new(linux.ENOMEDIUM, "no medium found") + EMEDIUMTYPE = new(linux.EMEDIUMTYPE, "wrong medium type") + ECANCELED = new(linux.ECANCELED, "operation Canceled") + ENOKEY = new(linux.ENOKEY, "required key not available") + EKEYEXPIRED = new(linux.EKEYEXPIRED, "key has expired") + EKEYREVOKED = new(linux.EKEYREVOKED, "key has been revoked") + EKEYREJECTED = new(linux.EKEYREJECTED, "key was rejected by service") + EOWNERDEAD = new(linux.EOWNERDEAD, "owner died") + ENOTRECOVERABLE = new(linux.ENOTRECOVERABLE, "state not recoverable") + ERFKILL = new(linux.ERFKILL, "operation not possible due to RF-kill") + EHWPOISON = new(linux.EHWPOISON, "memory page has hardware error") +) diff --git a/pkg/syserror/syserror_test.go b/pkg/linuxerr/linuxerr_test.go index c141e5f6e..d34937e93 100644 --- a/pkg/syserror/syserror_test.go +++ b/pkg/linuxerr/linuxerr_test.go @@ -19,6 +19,7 @@ import ( "testing" "golang.org/x/sys/unix" + "gvisor.dev/gvisor/pkg/linuxerr" "gvisor.dev/gvisor/pkg/syserror" ) @@ -30,7 +31,13 @@ func BenchmarkAssignErrno(b *testing.B) { } } -func BenchmarkAssignError(b *testing.B) { +func BenchmarkLinuxerrAssignError(b *testing.B) { + for i := b.N; i > 0; i-- { + globalError = linuxerr.EINVAL + } +} + +func BenchmarkAssignSyserrorError(b *testing.B) { for i := b.N; i > 0; i-- { globalError = syserror.EINVAL } @@ -46,7 +53,17 @@ func BenchmarkCompareErrno(b *testing.B) { } } -func BenchmarkCompareError(b *testing.B) { +func BenchmarkCompareLinuxerrError(b *testing.B) { + globalError = linuxerr.E2BIG + j := 0 + for i := b.N; i > 0; i-- { + if globalError == linuxerr.EINVAL { + j++ + } + } +} + +func BenchmarkCompareSyserrorError(b *testing.B) { globalError = syserror.EAGAIN j := 0 for i := b.N; i > 0; i-- { @@ -62,7 +79,7 @@ func BenchmarkSwitchErrno(b *testing.B) { for i := b.N; i > 0; i-- { switch globalError { case unix.EINVAL: - j += 1 + j++ case unix.EINTR: j += 2 case unix.EAGAIN: @@ -71,13 +88,28 @@ func BenchmarkSwitchErrno(b *testing.B) { } } -func BenchmarkSwitchError(b *testing.B) { +func BenchmarkSwitchLinuxerrError(b *testing.B) { + globalError = linuxerr.EPERM + j := 0 + for i := b.N; i > 0; i-- { + switch globalError { + case linuxerr.EINVAL: + j++ + case linuxerr.EINTR: + j += 2 + case linuxerr.EAGAIN: + j += 3 + } + } +} + +func BenchmarkSwitchSyserrorError(b *testing.B) { globalError = syserror.EPERM j := 0 for i := b.N; i > 0; i-- { switch globalError { case syserror.EINVAL: - j += 1 + j++ case syserror.EINTR: j += 2 case syserror.EAGAIN: diff --git a/pkg/sentry/fs/gofer/file.go b/pkg/sentry/fs/gofer/file.go index bcdb2dda2..819e140bc 100644 --- a/pkg/sentry/fs/gofer/file.go +++ b/pkg/sentry/fs/gofer/file.go @@ -92,7 +92,6 @@ func NewFile(ctx context.Context, dirent *fs.Dirent, name string, flags fs.FileF } if flags.Write { if err := dirent.Inode.CheckPermission(ctx, fs.PermMask{Execute: true}); err == nil { - fsmetric.GoferOpensWX.Increment() metric.SuspiciousOperationsMetric.Increment("opened_write_execute_file") log.Warningf("Opened a writable executable: %q", name) } diff --git a/pkg/sentry/fsimpl/gofer/regular_file.go b/pkg/sentry/fsimpl/gofer/regular_file.go index 0a954c138..eed05e369 100644 --- a/pkg/sentry/fsimpl/gofer/regular_file.go +++ b/pkg/sentry/fsimpl/gofer/regular_file.go @@ -60,7 +60,6 @@ func newRegularFileFD(mnt *vfs.Mount, d *dentry, flags uint32) (*regularFileFD, return nil, err } if fd.vfsfd.IsWritable() && (atomic.LoadUint32(&d.mode)&0111 != 0) { - fsmetric.GoferOpensWX.Increment() metric.SuspiciousOperationsMetric.Increment("opened_write_execute_file") } if atomic.LoadInt32(&d.mmapFD) >= 0 { diff --git a/pkg/sentry/fsimpl/gofer/special_file.go b/pkg/sentry/fsimpl/gofer/special_file.go index dc019ebd5..c12444b7e 100644 --- a/pkg/sentry/fsimpl/gofer/special_file.go +++ b/pkg/sentry/fsimpl/gofer/special_file.go @@ -101,7 +101,6 @@ func newSpecialFileFD(h handle, mnt *vfs.Mount, d *dentry, flags uint32) (*speci d.fs.specialFileFDs[fd] = struct{}{} d.fs.syncMu.Unlock() if fd.vfsfd.IsWritable() && (atomic.LoadUint32(&d.mode)&0111 != 0) { - fsmetric.GoferOpensWX.Increment() metric.SuspiciousOperationsMetric.Increment("opened_write_execute_file") } if h.fd >= 0 { diff --git a/pkg/sentry/fsmetric/fsmetric.go b/pkg/sentry/fsmetric/fsmetric.go index 7e535b527..17d0d5025 100644 --- a/pkg/sentry/fsmetric/fsmetric.go +++ b/pkg/sentry/fsmetric/fsmetric.go @@ -42,7 +42,6 @@ var ( // Metrics that only apply to fs/gofer and fsimpl/gofer. var ( - GoferOpensWX = metric.MustCreateNewUint64Metric("/gofer/opened_write_execute_file", true /* sync */, "Number of times a executable file was opened writably from a gofer.") GoferOpens9P = metric.MustCreateNewUint64Metric("/gofer/opens_9p", false /* sync */, "Number of times a file was opened from a gofer and did not have a host file descriptor.") GoferOpensHost = metric.MustCreateNewUint64Metric("/gofer/opens_host", false /* sync */, "Number of times a file was opened from a gofer and did have a host file descriptor.") GoferReads9P = metric.MustCreateNewUint64Metric("/gofer/reads_9p", false /* sync */, "Number of 9P file reads from a gofer.") diff --git a/pkg/shim/BUILD b/pkg/shim/BUILD index 4f7c02f5d..fd6127b97 100644 --- a/pkg/shim/BUILD +++ b/pkg/shim/BUILD @@ -1,4 +1,4 @@ -load("//tools:defs.bzl", "go_library") +load("//tools:defs.bzl", "go_library", "go_test") package(licenses = ["notice"]) @@ -41,7 +41,19 @@ go_library( "@com_github_containerd_fifo//:go_default_library", "@com_github_containerd_typeurl//:go_default_library", "@com_github_gogo_protobuf//types:go_default_library", + "@com_github_opencontainers_runtime_spec//specs-go:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", "@org_golang_x_sys//unix:go_default_library", ], ) + +go_test( + name = "shim_test", + size = "small", + srcs = ["service_test.go"], + library = ":shim", + deps = [ + "//pkg/shim/utils", + "@com_github_opencontainers_runtime_spec//specs-go:go_default_library", + ], +) diff --git a/pkg/shim/service.go b/pkg/shim/service.go index 9d9fa8ef6..1f9adcb65 100644 --- a/pkg/shim/service.go +++ b/pkg/shim/service.go @@ -22,6 +22,7 @@ import ( "os" "os/exec" "path/filepath" + "strings" "sync" "time" @@ -44,6 +45,7 @@ import ( "github.com/containerd/containerd/sys/reaper" "github.com/containerd/typeurl" "github.com/gogo/protobuf/types" + specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" "gvisor.dev/gvisor/pkg/cleanup" @@ -944,9 +946,19 @@ func newInit(path, workDir, namespace string, platform stdio.Platform, r *proc.C if err != nil { return nil, fmt.Errorf("read oci spec: %w", err) } - if err := utils.UpdateVolumeAnnotations(r.Bundle, spec); err != nil { + + updated, err := utils.UpdateVolumeAnnotations(spec) + if err != nil { return nil, fmt.Errorf("update volume annotations: %w", err) } + updated = updateCgroup(spec) || updated + + if updated { + if err := utils.WriteSpec(r.Bundle, spec); err != nil { + return nil, err + } + } + runsc.FormatRunscLogPath(r.ID, options.RunscConfig) runtime := proc.NewRunsc(options.Root, path, namespace, options.BinaryName, options.RunscConfig) p := proc.New(r.ID, runtime, stdio.Stdio{ @@ -966,3 +978,39 @@ func newInit(path, workDir, namespace string, platform stdio.Platform, r *proc.C p.Monitor = reaper.Default return p, nil } + +// updateCgroup updates cgroup path for the sandbox to make the sandbox join the +// pod cgroup and not the pause container cgroup. Returns true if the spec was +// modified. Ex.: +// /kubepods/burstable/pod123/abc => kubepods/burstable/pod123 +// +func updateCgroup(spec *specs.Spec) bool { + if !utils.IsSandbox(spec) { + return false + } + if spec.Linux == nil || len(spec.Linux.CgroupsPath) == 0 { + return false + } + + // Search backwards for the pod cgroup path to make the sandbox use it, + // instead of the pause container's cgroup. + parts := strings.Split(spec.Linux.CgroupsPath, string(filepath.Separator)) + for i := len(parts) - 1; i >= 0; i-- { + if strings.HasPrefix(parts[i], "pod") { + var path string + for j := 0; j <= i; j++ { + path = filepath.Join(path, parts[j]) + } + // Add back the initial '/' that may have been lost above. + if filepath.IsAbs(spec.Linux.CgroupsPath) { + path = string(filepath.Separator) + path + } + if spec.Linux.CgroupsPath == path { + return false + } + spec.Linux.CgroupsPath = path + return true + } + } + return false +} diff --git a/pkg/shim/service_test.go b/pkg/shim/service_test.go new file mode 100644 index 000000000..2d9f07e02 --- /dev/null +++ b/pkg/shim/service_test.go @@ -0,0 +1,121 @@ +// Copyright 2021 The gVisor Authors. +// +// 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 +// +// https://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 shim + +import ( + "testing" + + specs "github.com/opencontainers/runtime-spec/specs-go" + "gvisor.dev/gvisor/pkg/shim/utils" +) + +func TestCgroupPath(t *testing.T) { + for _, tc := range []struct { + name string + path string + want string + }{ + { + name: "simple", + path: "foo/pod123/container", + want: "foo/pod123", + }, + { + name: "absolute", + path: "/foo/pod123/container", + want: "/foo/pod123", + }, + { + name: "no-container", + path: "foo/pod123", + want: "foo/pod123", + }, + { + name: "no-container-absolute", + path: "/foo/pod123", + want: "/foo/pod123", + }, + { + name: "double-pod", + path: "/foo/podium/pod123/container", + want: "/foo/podium/pod123", + }, + { + name: "start-pod", + path: "pod123/container", + want: "pod123", + }, + { + name: "start-pod-absolute", + path: "/pod123/container", + want: "/pod123", + }, + { + name: "slashes", + path: "///foo/////pod123//////container", + want: "/foo/pod123", + }, + { + name: "no-pod", + path: "/foo/nopod123/container", + want: "/foo/nopod123/container", + }, + } { + t.Run(tc.name, func(t *testing.T) { + spec := specs.Spec{ + Linux: &specs.Linux{ + CgroupsPath: tc.path, + }, + } + updated := updateCgroup(&spec) + if spec.Linux.CgroupsPath != tc.want { + t.Errorf("updateCgroup(%q), want: %q, got: %q", tc.path, tc.want, spec.Linux.CgroupsPath) + } + if shouldUpdate := tc.path != tc.want; shouldUpdate != updated { + t.Errorf("updateCgroup(%q)=%v, want: %v", tc.path, updated, shouldUpdate) + } + }) + } +} + +// Test cases that cgroup path should not be updated. +func TestCgroupNoUpdate(t *testing.T) { + for _, tc := range []struct { + name string + spec *specs.Spec + }{ + { + name: "empty", + spec: &specs.Spec{}, + }, + { + name: "subcontainer", + spec: &specs.Spec{ + Linux: &specs.Linux{ + CgroupsPath: "foo/pod123/container", + }, + Annotations: map[string]string{ + utils.ContainerTypeAnnotation: utils.ContainerTypeContainer, + }, + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + if updated := updateCgroup(tc.spec); updated { + t.Errorf("updateCgroup(%+v), got: %v, want: false", tc.spec.Linux, updated) + } + }) + } +} diff --git a/pkg/shim/utils/annotations.go b/pkg/shim/utils/annotations.go index 1e9d3f365..c744800bb 100644 --- a/pkg/shim/utils/annotations.go +++ b/pkg/shim/utils/annotations.go @@ -19,7 +19,9 @@ package utils // These are vendor due to import conflicts. const ( sandboxLogDirAnnotation = "io.kubernetes.cri.sandbox-log-directory" - containerTypeAnnotation = "io.kubernetes.cri.container-type" + // ContainerTypeAnnotation is they key that defines sandbox or container. + ContainerTypeAnnotation = "io.kubernetes.cri.container-type" containerTypeSandbox = "sandbox" - containerTypeContainer = "container" + // ContainerTypeContainer is the value for container. + ContainerTypeContainer = "container" ) diff --git a/pkg/shim/utils/utils.go b/pkg/shim/utils/utils.go index 7b1cd983e..f183b1bbc 100644 --- a/pkg/shim/utils/utils.go +++ b/pkg/shim/utils/utils.go @@ -18,19 +18,16 @@ package utils import ( "encoding/json" "io/ioutil" - "os" "path/filepath" specs "github.com/opencontainers/runtime-spec/specs-go" ) +const configFilename = "config.json" + // ReadSpec reads OCI spec from the bundle directory. func ReadSpec(bundle string) (*specs.Spec, error) { - f, err := os.Open(filepath.Join(bundle, "config.json")) - if err != nil { - return nil, err - } - b, err := ioutil.ReadAll(f) + b, err := ioutil.ReadFile(filepath.Join(bundle, configFilename)) if err != nil { return nil, err } @@ -41,9 +38,18 @@ func ReadSpec(bundle string) (*specs.Spec, error) { return &spec, nil } +// WriteSpec writes OCI spec to the bundle directory. +func WriteSpec(bundle string, spec *specs.Spec) error { + b, err := json.Marshal(spec) + if err != nil { + return err + } + return ioutil.WriteFile(filepath.Join(bundle, configFilename), b, 0666) +} + // IsSandbox checks whether a container is a sandbox container. func IsSandbox(spec *specs.Spec) bool { - t, ok := spec.Annotations[containerTypeAnnotation] + t, ok := spec.Annotations[ContainerTypeAnnotation] return !ok || t == containerTypeSandbox } diff --git a/pkg/shim/utils/volumes.go b/pkg/shim/utils/volumes.go index cdcb88229..6bc75139d 100644 --- a/pkg/shim/utils/volumes.go +++ b/pkg/shim/utils/volumes.go @@ -15,9 +15,7 @@ package utils import ( - "encoding/json" "fmt" - "io/ioutil" "path/filepath" "strings" @@ -89,8 +87,8 @@ func isVolumePath(volume, path string) (bool, error) { } // UpdateVolumeAnnotations add necessary OCI annotations for gvisor -// volume optimization. -func UpdateVolumeAnnotations(bundle string, s *specs.Spec) error { +// volume optimization. Returns true if the spec was modified. +func UpdateVolumeAnnotations(s *specs.Spec) (bool, error) { var uid string if IsSandbox(s) { var err error @@ -98,7 +96,7 @@ func UpdateVolumeAnnotations(bundle string, s *specs.Spec) error { if err != nil { // Skip if we can't get pod UID, because this doesn't work // for containerd 1.1. - return nil + return false, nil } } var updated bool @@ -114,7 +112,7 @@ func UpdateVolumeAnnotations(bundle string, s *specs.Spec) error { // This is a sandbox. path, err := volumePath(volume, uid) if err != nil { - return fmt.Errorf("get volume path for %q: %w", volume, err) + return false, fmt.Errorf("get volume path for %q: %w", volume, err) } s.Annotations[volumeSourceKey(volume)] = path updated = true @@ -138,15 +136,7 @@ func UpdateVolumeAnnotations(bundle string, s *specs.Spec) error { } } } - if !updated { - return nil - } - // Update bundle. - b, err := json.Marshal(s) - if err != nil { - return err - } - return ioutil.WriteFile(filepath.Join(bundle, "config.json"), b, 0666) + return updated, nil } func changeMountType(m *specs.Mount, newType string) { diff --git a/pkg/shim/utils/volumes_test.go b/pkg/shim/utils/volumes_test.go index b25c53c73..5db43cdf1 100644 --- a/pkg/shim/utils/volumes_test.go +++ b/pkg/shim/utils/volumes_test.go @@ -15,11 +15,9 @@ package utils import ( - "encoding/json" "fmt" "io/ioutil" "os" - "path/filepath" "reflect" "testing" @@ -58,7 +56,7 @@ func TestUpdateVolumeAnnotations(t *testing.T) { spec: &specs.Spec{ Annotations: map[string]string{ sandboxLogDirAnnotation: testLogDirPath, - containerTypeAnnotation: containerTypeSandbox, + ContainerTypeAnnotation: containerTypeSandbox, volumeKeyPrefix + testVolumeName + ".share": "pod", volumeKeyPrefix + testVolumeName + ".type": "tmpfs", volumeKeyPrefix + testVolumeName + ".options": "ro", @@ -67,7 +65,7 @@ func TestUpdateVolumeAnnotations(t *testing.T) { expected: &specs.Spec{ Annotations: map[string]string{ sandboxLogDirAnnotation: testLogDirPath, - containerTypeAnnotation: containerTypeSandbox, + ContainerTypeAnnotation: containerTypeSandbox, volumeKeyPrefix + testVolumeName + ".share": "pod", volumeKeyPrefix + testVolumeName + ".type": "tmpfs", volumeKeyPrefix + testVolumeName + ".options": "ro", @@ -81,7 +79,7 @@ func TestUpdateVolumeAnnotations(t *testing.T) { spec: &specs.Spec{ Annotations: map[string]string{ sandboxLogDirAnnotation: testLegacyLogDirPath, - containerTypeAnnotation: containerTypeSandbox, + ContainerTypeAnnotation: containerTypeSandbox, volumeKeyPrefix + testVolumeName + ".share": "pod", volumeKeyPrefix + testVolumeName + ".type": "tmpfs", volumeKeyPrefix + testVolumeName + ".options": "ro", @@ -90,7 +88,7 @@ func TestUpdateVolumeAnnotations(t *testing.T) { expected: &specs.Spec{ Annotations: map[string]string{ sandboxLogDirAnnotation: testLegacyLogDirPath, - containerTypeAnnotation: containerTypeSandbox, + ContainerTypeAnnotation: containerTypeSandbox, volumeKeyPrefix + testVolumeName + ".share": "pod", volumeKeyPrefix + testVolumeName + ".type": "tmpfs", volumeKeyPrefix + testVolumeName + ".options": "ro", @@ -117,7 +115,7 @@ func TestUpdateVolumeAnnotations(t *testing.T) { }, }, Annotations: map[string]string{ - containerTypeAnnotation: containerTypeContainer, + ContainerTypeAnnotation: ContainerTypeContainer, volumeKeyPrefix + testVolumeName + ".share": "pod", volumeKeyPrefix + testVolumeName + ".type": "tmpfs", volumeKeyPrefix + testVolumeName + ".options": "ro", @@ -139,7 +137,7 @@ func TestUpdateVolumeAnnotations(t *testing.T) { }, }, Annotations: map[string]string{ - containerTypeAnnotation: containerTypeContainer, + ContainerTypeAnnotation: ContainerTypeContainer, volumeKeyPrefix + testVolumeName + ".share": "pod", volumeKeyPrefix + testVolumeName + ".type": "tmpfs", volumeKeyPrefix + testVolumeName + ".options": "ro", @@ -159,7 +157,7 @@ func TestUpdateVolumeAnnotations(t *testing.T) { }, }, Annotations: map[string]string{ - containerTypeAnnotation: containerTypeContainer, + ContainerTypeAnnotation: ContainerTypeContainer, volumeKeyPrefix + testVolumeName + ".share": "container", volumeKeyPrefix + testVolumeName + ".type": "bind", volumeKeyPrefix + testVolumeName + ".options": "ro", @@ -175,7 +173,7 @@ func TestUpdateVolumeAnnotations(t *testing.T) { }, }, Annotations: map[string]string{ - containerTypeAnnotation: containerTypeContainer, + ContainerTypeAnnotation: ContainerTypeContainer, volumeKeyPrefix + testVolumeName + ".share": "container", volumeKeyPrefix + testVolumeName + ".type": "bind", volumeKeyPrefix + testVolumeName + ".options": "ro", @@ -187,7 +185,7 @@ func TestUpdateVolumeAnnotations(t *testing.T) { name: "should not return error without pod log directory", spec: &specs.Spec{ Annotations: map[string]string{ - containerTypeAnnotation: containerTypeSandbox, + ContainerTypeAnnotation: containerTypeSandbox, volumeKeyPrefix + testVolumeName + ".share": "pod", volumeKeyPrefix + testVolumeName + ".type": "tmpfs", volumeKeyPrefix + testVolumeName + ".options": "ro", @@ -195,7 +193,7 @@ func TestUpdateVolumeAnnotations(t *testing.T) { }, expected: &specs.Spec{ Annotations: map[string]string{ - containerTypeAnnotation: containerTypeSandbox, + ContainerTypeAnnotation: containerTypeSandbox, volumeKeyPrefix + testVolumeName + ".share": "pod", volumeKeyPrefix + testVolumeName + ".type": "tmpfs", volumeKeyPrefix + testVolumeName + ".options": "ro", @@ -207,7 +205,7 @@ func TestUpdateVolumeAnnotations(t *testing.T) { spec: &specs.Spec{ Annotations: map[string]string{ sandboxLogDirAnnotation: testLogDirPath, - containerTypeAnnotation: containerTypeSandbox, + ContainerTypeAnnotation: containerTypeSandbox, volumeKeyPrefix + "notexist.share": "pod", volumeKeyPrefix + "notexist.type": "tmpfs", volumeKeyPrefix + "notexist.options": "ro", @@ -220,13 +218,13 @@ func TestUpdateVolumeAnnotations(t *testing.T) { spec: &specs.Spec{ Annotations: map[string]string{ sandboxLogDirAnnotation: testLogDirPath, - containerTypeAnnotation: containerTypeSandbox, + ContainerTypeAnnotation: containerTypeSandbox, }, }, expected: &specs.Spec{ Annotations: map[string]string{ sandboxLogDirAnnotation: testLogDirPath, - containerTypeAnnotation: containerTypeSandbox, + ContainerTypeAnnotation: containerTypeSandbox, }, }, }, @@ -248,7 +246,7 @@ func TestUpdateVolumeAnnotations(t *testing.T) { }, }, Annotations: map[string]string{ - containerTypeAnnotation: containerTypeContainer, + ContainerTypeAnnotation: ContainerTypeContainer, }, }, expected: &specs.Spec{ @@ -267,7 +265,7 @@ func TestUpdateVolumeAnnotations(t *testing.T) { }, }, Annotations: map[string]string{ - containerTypeAnnotation: containerTypeContainer, + ContainerTypeAnnotation: ContainerTypeContainer, }, }, }, @@ -275,7 +273,7 @@ func TestUpdateVolumeAnnotations(t *testing.T) { name: "bind options removed", spec: &specs.Spec{ Annotations: map[string]string{ - containerTypeAnnotation: containerTypeContainer, + ContainerTypeAnnotation: ContainerTypeContainer, volumeKeyPrefix + testVolumeName + ".share": "pod", volumeKeyPrefix + testVolumeName + ".type": "tmpfs", volumeKeyPrefix + testVolumeName + ".options": "ro", @@ -292,7 +290,7 @@ func TestUpdateVolumeAnnotations(t *testing.T) { }, expected: &specs.Spec{ Annotations: map[string]string{ - containerTypeAnnotation: containerTypeContainer, + ContainerTypeAnnotation: ContainerTypeContainer, volumeKeyPrefix + testVolumeName + ".share": "pod", volumeKeyPrefix + testVolumeName + ".type": "tmpfs", volumeKeyPrefix + testVolumeName + ".options": "ro", @@ -311,11 +309,7 @@ func TestUpdateVolumeAnnotations(t *testing.T) { }, } { t.Run(test.name, func(t *testing.T) { - bundle, err := ioutil.TempDir(dir, "test-bundle") - if err != nil { - t.Fatalf("Create test bundle: %v", err) - } - err = UpdateVolumeAnnotations(bundle, test.spec) + updated, err := UpdateVolumeAnnotations(test.spec) if test.expectErr { if err == nil { t.Fatal("Expected error, but got nil") @@ -328,18 +322,8 @@ func TestUpdateVolumeAnnotations(t *testing.T) { if !reflect.DeepEqual(test.expected, test.spec) { t.Fatalf("Expected %+v, got %+v", test.expected, test.spec) } - if test.expectUpdate { - b, err := ioutil.ReadFile(filepath.Join(bundle, "config.json")) - if err != nil { - t.Fatalf("Read spec from bundle: %v", err) - } - var spec specs.Spec - if err := json.Unmarshal(b, &spec); err != nil { - t.Fatalf("Unmarshal spec: %v", err) - } - if !reflect.DeepEqual(test.expected, &spec) { - t.Fatalf("Expected %+v, got %+v", test.expected, &spec) - } + if test.expectUpdate != updated { + t.Errorf("Expected %v, got %v", test.expected, updated) } }) } diff --git a/pkg/syserror/BUILD b/pkg/syserror/BUILD index 7d2f5adf6..76bee5a64 100644 --- a/pkg/syserror/BUILD +++ b/pkg/syserror/BUILD @@ -1,4 +1,4 @@ -load("//tools:defs.bzl", "go_library", "go_test") +load("//tools:defs.bzl", "go_library") package(licenses = ["notice"]) @@ -8,12 +8,3 @@ go_library( visibility = ["//visibility:public"], deps = ["@org_golang_x_sys//unix:go_default_library"], ) - -go_test( - name = "syserror_test", - srcs = ["syserror_test.go"], - deps = [ - ":syserror", - "@org_golang_x_sys//unix:go_default_library", - ], -) diff --git a/pkg/tcpip/stack/packet_buffer.go b/pkg/tcpip/stack/packet_buffer.go index 01652fbe7..4ca702121 100644 --- a/pkg/tcpip/stack/packet_buffer.go +++ b/pkg/tcpip/stack/packet_buffer.go @@ -245,10 +245,10 @@ func (pk *PacketBuffer) dataOffset() int { func (pk *PacketBuffer) push(typ headerType, size int) tcpipbuffer.View { h := &pk.headers[typ] if h.length > 0 { - panic(fmt.Sprintf("push must not be called twice: type %s", typ)) + panic(fmt.Sprintf("push(%s, %d) called after previous push", typ, size)) } if pk.pushed+size > pk.reserved { - panic("not enough headroom reserved") + panic(fmt.Sprintf("push(%s, %d) overflows; pushed=%d reserved=%d", typ, size, pk.pushed, pk.reserved)) } pk.pushed += size h.offset = -pk.pushed diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index 729b4c63b..c7d0f9036 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -1489,6 +1489,7 @@ cc_binary( "//test/util:cleanup", "//test/util:posix_error", "//test/util:pty_util", + "//test/util:signal_util", "//test/util:test_main", "//test/util:test_util", "//test/util:thread_util", diff --git a/test/syscalls/linux/pty.cc b/test/syscalls/linux/pty.cc index 8d15c491e..1c83e2656 100644 --- a/test/syscalls/linux/pty.cc +++ b/test/syscalls/linux/pty.cc @@ -40,6 +40,7 @@ #include "test/util/file_descriptor.h" #include "test/util/posix_error.h" #include "test/util/pty_util.h" +#include "test/util/signal_util.h" #include "test/util/test_util.h" #include "test/util/thread_util.h" @@ -387,6 +388,22 @@ PosixErrorOr<size_t> PollAndReadFd(int fd, void* buf, size_t count, } TEST(PtyTrunc, Truncate) { + SKIP_IF(IsRunningWithVFS1()); + + // setsid either puts us in a new session or fails because we're already the + // session leader. Either way, this ensures we're the session leader and have + // no controlling terminal. + setsid(); + + // Make sure we're ignoring SIGHUP, which will be sent to this process once we + // disconnect the TTY. + struct sigaction sa = {}; + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + const Cleanup cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGHUP, sa)); + // Opening PTYs with O_TRUNC shouldn't cause an error, but calls to // (f)truncate should. FileDescriptor master = @@ -395,6 +412,7 @@ TEST(PtyTrunc, Truncate) { std::string spath = absl::StrCat("/dev/pts/", n); FileDescriptor replica = ASSERT_NO_ERRNO_AND_VALUE(Open(spath, O_RDWR | O_NONBLOCK | O_TRUNC)); + ASSERT_THAT(ioctl(replica.get(), TIOCNOTTY), SyscallSucceeds()); EXPECT_THAT(truncate(kMasterPath, 0), SyscallFailsWithErrno(EINVAL)); EXPECT_THAT(truncate(spath.c_str(), 0), SyscallFailsWithErrno(EINVAL)); @@ -467,7 +485,7 @@ TEST(BasicPtyTest, OpenSetsControllingTTY) { setsid(); // Make sure we're ignoring SIGHUP, which will be sent to this process once we - // disconnect they TTY. + // disconnect the TTY. struct sigaction sa = {}; sa.sa_handler = SIG_IGN; sa.sa_flags = 0; @@ -1405,7 +1423,7 @@ TEST_F(JobControlTest, ReleaseTTY) { ASSERT_THAT(ioctl(replica_.get(), TIOCSCTTY, 0), SyscallSucceeds()); // Make sure we're ignoring SIGHUP, which will be sent to this process once we - // disconnect they TTY. + // disconnect the TTY. struct sigaction sa = {}; sa.sa_handler = SIG_IGN; sa.sa_flags = 0; @@ -1526,7 +1544,7 @@ TEST_F(JobControlTest, ReleaseTTYSignals) { EXPECT_THAT(setpgid(diff_pgrp_child, diff_pgrp_child), SyscallSucceeds()); // Make sure we're ignoring SIGHUP, which will be sent to this process once we - // disconnect they TTY. + // disconnect the TTY. struct sigaction sighup_sa = {}; sighup_sa.sa_handler = SIG_IGN; sighup_sa.sa_flags = 0; diff --git a/test/syscalls/linux/socket_ip_tcp_generic.cc b/test/syscalls/linux/socket_ip_tcp_generic.cc index 59b56dc1a..2f5743cda 100644 --- a/test/syscalls/linux/socket_ip_tcp_generic.cc +++ b/test/syscalls/linux/socket_ip_tcp_generic.cc @@ -1155,7 +1155,7 @@ TEST_P(TCPSocketPairTest, IpMulticastLoopDefault) { TEST_P(TCPSocketPairTest, TCPResetDuringClose) { DisableSave ds; // Too many syscalls. - constexpr int kThreadCount = 1000; + constexpr int kThreadCount = 100; std::unique_ptr<ScopedThread> instances[kThreadCount]; for (int i = 0; i < kThreadCount; i++) { instances[i] = absl::make_unique<ScopedThread>([&]() { |