github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/contexttest/contexttest.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package contexttest builds a test context.Context.
    16  package contexttest
    17  
    18  import (
    19  	"os"
    20  	"sync/atomic"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/SagerNet/gvisor/pkg/context"
    25  	"github.com/SagerNet/gvisor/pkg/memutil"
    26  	"github.com/SagerNet/gvisor/pkg/sentry/kernel/auth"
    27  	ktime "github.com/SagerNet/gvisor/pkg/sentry/kernel/time"
    28  	"github.com/SagerNet/gvisor/pkg/sentry/limits"
    29  	"github.com/SagerNet/gvisor/pkg/sentry/pgalloc"
    30  	"github.com/SagerNet/gvisor/pkg/sentry/platform"
    31  	"github.com/SagerNet/gvisor/pkg/sentry/platform/ptrace"
    32  	"github.com/SagerNet/gvisor/pkg/sentry/uniqueid"
    33  )
    34  
    35  // Context returns a Context that may be used in tests. Uses ptrace as the
    36  // platform.Platform.
    37  //
    38  // Note that some filesystems may require a minimal kernel for testing, which
    39  // this test context does not provide. For such tests, see kernel/contexttest.
    40  func Context(tb testing.TB) context.Context {
    41  	const memfileName = "contexttest-memory"
    42  	memfd, err := memutil.CreateMemFD(memfileName, 0)
    43  	if err != nil {
    44  		tb.Fatalf("error creating application memory file: %v", err)
    45  	}
    46  	memfile := os.NewFile(uintptr(memfd), memfileName)
    47  	mf, err := pgalloc.NewMemoryFile(memfile, pgalloc.MemoryFileOpts{})
    48  	if err != nil {
    49  		memfile.Close()
    50  		tb.Fatalf("error creating pgalloc.MemoryFile: %v", err)
    51  	}
    52  	p, err := ptrace.New()
    53  	if err != nil {
    54  		tb.Fatal(err)
    55  	}
    56  	// Test usage of context.Background is fine.
    57  	return &TestContext{
    58  		Context:     context.Background(),
    59  		l:           limits.NewLimitSet(),
    60  		mf:          mf,
    61  		platform:    p,
    62  		creds:       auth.NewAnonymousCredentials(),
    63  		otherValues: make(map[interface{}]interface{}),
    64  	}
    65  }
    66  
    67  // TestContext represents a context with minimal functionality suitable for
    68  // running tests.
    69  type TestContext struct {
    70  	context.Context
    71  	l           *limits.LimitSet
    72  	mf          *pgalloc.MemoryFile
    73  	platform    platform.Platform
    74  	creds       *auth.Credentials
    75  	otherValues map[interface{}]interface{}
    76  }
    77  
    78  // globalUniqueID tracks incremental unique identifiers for tests.
    79  var globalUniqueID uint64
    80  
    81  // globalUniqueIDProvider implements unix.UniqueIDProvider.
    82  type globalUniqueIDProvider struct{}
    83  
    84  // UniqueID implements unix.UniqueIDProvider.UniqueID.
    85  func (*globalUniqueIDProvider) UniqueID() uint64 {
    86  	return atomic.AddUint64(&globalUniqueID, 1)
    87  }
    88  
    89  // lastInotifyCookie is a monotonically increasing counter for generating unique
    90  // inotify cookies. Must be accessed using atomic ops.
    91  var lastInotifyCookie uint32
    92  
    93  // hostClock implements ktime.Clock.
    94  type hostClock struct {
    95  	ktime.WallRateClock
    96  	ktime.NoClockEvents
    97  }
    98  
    99  // Now implements ktime.Clock.Now.
   100  func (*hostClock) Now() ktime.Time {
   101  	return ktime.FromNanoseconds(time.Now().UnixNano())
   102  }
   103  
   104  // RegisterValue registers additional values with this test context. Useful for
   105  // providing values from external packages that contexttest can't depend on.
   106  func (t *TestContext) RegisterValue(key, value interface{}) {
   107  	t.otherValues[key] = value
   108  }
   109  
   110  // Value implements context.Context.
   111  func (t *TestContext) Value(key interface{}) interface{} {
   112  	switch key {
   113  	case auth.CtxCredentials:
   114  		return t.creds
   115  	case limits.CtxLimits:
   116  		return t.l
   117  	case pgalloc.CtxMemoryFile:
   118  		return t.mf
   119  	case pgalloc.CtxMemoryFileProvider:
   120  		return t
   121  	case platform.CtxPlatform:
   122  		return t.platform
   123  	case uniqueid.CtxGlobalUniqueID:
   124  		return (*globalUniqueIDProvider).UniqueID(nil)
   125  	case uniqueid.CtxGlobalUniqueIDProvider:
   126  		return &globalUniqueIDProvider{}
   127  	case uniqueid.CtxInotifyCookie:
   128  		return atomic.AddUint32(&lastInotifyCookie, 1)
   129  	case ktime.CtxRealtimeClock:
   130  		return &hostClock{}
   131  	default:
   132  		if val, ok := t.otherValues[key]; ok {
   133  			return val
   134  		}
   135  		return t.Context.Value(key)
   136  	}
   137  }
   138  
   139  // MemoryFile implements pgalloc.MemoryFileProvider.MemoryFile.
   140  func (t *TestContext) MemoryFile() *pgalloc.MemoryFile {
   141  	return t.mf
   142  }
   143  
   144  // RootContext returns a Context that may be used in tests that need root
   145  // credentials. Uses ptrace as the platform.Platform.
   146  func RootContext(tb testing.TB) context.Context {
   147  	return auth.ContextWithCredentials(Context(tb), auth.NewRootCredentials(auth.NewRootUserNamespace()))
   148  }
   149  
   150  // WithLimitSet returns a copy of ctx carrying l.
   151  func WithLimitSet(ctx context.Context, l *limits.LimitSet) context.Context {
   152  	return limitContext{ctx, l}
   153  }
   154  
   155  type limitContext struct {
   156  	context.Context
   157  	l *limits.LimitSet
   158  }
   159  
   160  // Value implements context.Context.
   161  func (lc limitContext) Value(key interface{}) interface{} {
   162  	switch key {
   163  	case limits.CtxLimits:
   164  		return lc.l
   165  	default:
   166  		return lc.Context.Value(key)
   167  	}
   168  }