diff options
Diffstat (limited to 'pkg/sentry/context')
-rw-r--r-- | pkg/sentry/context/BUILD | 14 | ||||
-rw-r--r-- | pkg/sentry/context/context.go | 103 | ||||
-rw-r--r-- | pkg/sentry/context/contexttest/BUILD | 34 | ||||
-rw-r--r-- | pkg/sentry/context/contexttest/contexttest.go | 133 |
4 files changed, 284 insertions, 0 deletions
diff --git a/pkg/sentry/context/BUILD b/pkg/sentry/context/BUILD new file mode 100644 index 000000000..ff39f94ba --- /dev/null +++ b/pkg/sentry/context/BUILD @@ -0,0 +1,14 @@ +package(licenses = ["notice"]) # Apache 2.0 + +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "context", + srcs = ["context.go"], + importpath = "gvisor.googlesource.com/gvisor/pkg/sentry/context", + visibility = ["//pkg/sentry:internal"], + deps = [ + "//pkg/amutex", + "//pkg/log", + ], +) diff --git a/pkg/sentry/context/context.go b/pkg/sentry/context/context.go new file mode 100644 index 000000000..e0dffafba --- /dev/null +++ b/pkg/sentry/context/context.go @@ -0,0 +1,103 @@ +// 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 context defines the sentry's Context type. +package context + +import ( + "gvisor.googlesource.com/gvisor/pkg/amutex" + "gvisor.googlesource.com/gvisor/pkg/log" +) + +// A Context represents a thread of execution (hereafter "goroutine" to reflect +// Go idiosyncrasy). It carries state associated with the goroutine across API +// boundaries. +// +// While Context exists for essentially the same reasons as Go's standard +// context.Context, the standard type represents the state of an operation +// rather than that of a goroutine. This is a critical distinction: +// +// - Unlike context.Context, which "may be passed to functions running in +// different goroutines", it is *not safe* to use the same Context in multiple +// concurrent goroutines. +// +// - It is *not safe* to retain a Context passed to a function beyond the scope +// of that function call. +// +// In both cases, values extracted from the Context should be used instead. +type Context interface { + log.Logger + amutex.Sleeper + + // UninterruptibleSleepStart indicates the beginning of an uninterruptible + // sleep state (equivalent to Linux's TASK_UNINTERRUPTIBLE). If deactivate + // is true and the Context represents a Task, the Task's AddressSpace is + // deactivated. + UninterruptibleSleepStart(deactivate bool) + + // UninterruptibleSleepFinish indicates the end of an uninterruptible sleep + // state that was begun by a previous call to UninterruptibleSleepStart. If + // activate is true and the Context represents a Task, the Task's + // AddressSpace is activated. Normally activate is the same value as the + // deactivate parameter passed to UninterruptibleSleepStart. + UninterruptibleSleepFinish(activate bool) + + // Value returns the value associated with this Context for key, or nil if + // no value is associated with key. Successive calls to Value with the same + // key returns the same result. + // + // A key identifies a specific value in a Context. Functions that wish to + // retrieve values from Context typically allocate a key in a global + // variable then use that key as the argument to Context.Value. A key can + // be any type that supports equality; packages should define keys as an + // unexported type to avoid collisions. + Value(key interface{}) interface{} +} + +type logContext struct { + log.Logger + NoopSleeper +} + +// Value implements Context.Value. +func (logContext) Value(key interface{}) interface{} { + return nil +} + +// NoopSleeper is a noop implementation of amutex.Sleeper and +// Context.UninterruptibleSleep* methods for anonymous embedding in other types +// that do not want to notify kernel.Task about sleeps. +type NoopSleeper struct { + amutex.NoopSleeper +} + +// UninterruptibleSleepStart does nothing. +func (NoopSleeper) UninterruptibleSleepStart(bool) {} + +// UninterruptibleSleepFinish does nothing. +func (NoopSleeper) UninterruptibleSleepFinish(bool) {} + +// Background returns an empty context using the default logger. +// +// Users should be wary of using a Background context. Please tag any use with +// FIXME and a note to remove this use. +// +// Generally, one should use the Task as their context when available, or avoid +// having to use a context in places where a Task is unavailable. +// +// Using a Background context for tests is fine, as long as no values are +// needed from the context in the tested code paths. +func Background() Context { + return logContext{Logger: log.Log()} +} diff --git a/pkg/sentry/context/contexttest/BUILD b/pkg/sentry/context/contexttest/BUILD new file mode 100644 index 000000000..5977344de --- /dev/null +++ b/pkg/sentry/context/contexttest/BUILD @@ -0,0 +1,34 @@ +package(licenses = ["notice"]) # Apache 2.0 + +load("@io_bazel_rules_go//go:def.bzl", "go_library") +load("//tools/go_stateify:defs.bzl", "go_stateify") + +go_stateify( + name = "contexttest_state", + srcs = [ + "contexttest.go", + ], + out = "contexttest_state.go", + package = "contexttest", +) + +go_library( + name = "contexttest", + testonly = 1, + srcs = [ + "contexttest.go", + "contexttest_state.go", + ], + importpath = "gvisor.googlesource.com/gvisor/pkg/sentry/context/contexttest", + visibility = ["//pkg/sentry:internal"], + deps = [ + "//pkg/sentry/context", + "//pkg/sentry/kernel/auth", + "//pkg/sentry/kernel/time", + "//pkg/sentry/limits", + "//pkg/sentry/platform", + "//pkg/sentry/platform/ptrace", + "//pkg/sentry/uniqueid", + "//pkg/state", + ], +) diff --git a/pkg/sentry/context/contexttest/contexttest.go b/pkg/sentry/context/contexttest/contexttest.go new file mode 100644 index 000000000..193ce3440 --- /dev/null +++ b/pkg/sentry/context/contexttest/contexttest.go @@ -0,0 +1,133 @@ +// 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 contexttest builds a test context.Context. +package contexttest + +import ( + "sync/atomic" + "testing" + "time" + + "gvisor.googlesource.com/gvisor/pkg/sentry/context" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth" + ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time" + "gvisor.googlesource.com/gvisor/pkg/sentry/limits" + "gvisor.googlesource.com/gvisor/pkg/sentry/platform" + "gvisor.googlesource.com/gvisor/pkg/sentry/platform/ptrace" + "gvisor.googlesource.com/gvisor/pkg/sentry/uniqueid" +) + +// Context returns a Context that may be used in tests. Uses ptrace as the +// platform.Platform. +func Context(tb testing.TB) context.Context { + p, err := ptrace.New() + if err != nil { + tb.Fatal(err) + } + // Test usage of context.Background is fine. + return &testContext{ + Context: context.Background(), + l: limits.NewLimitSet(), + platform: p, + } +} + +type testContext struct { + context.Context + l *limits.LimitSet + platform platform.Platform +} + +// globalUniqueID tracks incremental unique identifiers for tests. +var globalUniqueID uint64 + +// lastInotifyCookie is a monotonically increasing counter for generating unique +// inotify cookies. Must be accessed using atomic ops. +var lastInotifyCookie uint32 + +// hostClock implements ktime.Clock. +type hostClock struct { + ktime.WallRateClock + ktime.NoClockEvents +} + +// Now implements ktime.Clock.Now. +func (hostClock) Now() ktime.Time { + return ktime.FromNanoseconds(time.Now().UnixNano()) +} + +// Value implements context.Context. +func (t *testContext) Value(key interface{}) interface{} { + switch key { + case limits.CtxLimits: + return t.l + case platform.CtxPlatform: + return t.platform + case uniqueid.CtxGlobalUniqueID: + return atomic.AddUint64(&globalUniqueID, 1) + case uniqueid.CtxInotifyCookie: + return atomic.AddUint32(&lastInotifyCookie, 1) + case ktime.CtxRealtimeClock: + return hostClock{} + default: + return t.Context.Value(key) + } +} + +// RootContext returns a Context that may be used in tests that need root +// credentials. Uses ptrace as the platform.Platform. +func RootContext(tb testing.TB) context.Context { + return WithCreds(Context(tb), auth.NewRootCredentials(auth.NewRootUserNamespace())) +} + +// WithCreds returns a copy of ctx carrying creds. +func WithCreds(ctx context.Context, creds *auth.Credentials) context.Context { + return &authContext{ctx, creds} +} + +type authContext struct { + context.Context + creds *auth.Credentials +} + +// Value implements context.Context. +func (ac *authContext) Value(key interface{}) interface{} { + switch key { + case auth.CtxCredentials: + return ac.creds + default: + return ac.Context.Value(key) + } +} + +// WithLimitSet returns a copy of ctx carrying l. +func WithLimitSet(ctx context.Context, l *limits.LimitSet) context.Context { + return limitContext{ctx, l} +} + +type limitContext struct { + context.Context + l *limits.LimitSet +} + +// Value implements context.Context. +func (lc limitContext) Value(key interface{}) interface{} { + switch key { + case limits.CtxLimits: + return lc.l + default: + return lc.Context.Value(key) + } +} |