gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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  	"testing"
    21  	"time"
    22  
    23  	"gvisor.dev/gvisor/pkg/atomicbitops"
    24  	"gvisor.dev/gvisor/pkg/context"
    25  	"gvisor.dev/gvisor/pkg/memutil"
    26  	"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
    27  	ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
    28  	"gvisor.dev/gvisor/pkg/sentry/limits"
    29  	"gvisor.dev/gvisor/pkg/sentry/pgalloc"
    30  	"gvisor.dev/gvisor/pkg/sentry/platform"
    31  	"gvisor.dev/gvisor/pkg/sentry/platform/ptrace"
    32  	"gvisor.dev/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[any]any),
    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[any]any
    76  }
    77  
    78  // globalUniqueID tracks incremental unique identifiers for tests.
    79  var globalUniqueID atomicbitops.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 globalUniqueID.Add(1)
    87  }
    88  
    89  // lastInotifyCookie is a monotonically increasing counter for generating unique
    90  // inotify cookies.
    91  var lastInotifyCookie atomicbitops.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 any) {
   107  	t.otherValues[key] = value
   108  }
   109  
   110  // Value implements context.Context.
   111  func (t *TestContext) Value(key any) any {
   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 platform.CtxPlatform:
   120  		return t.platform
   121  	case uniqueid.CtxGlobalUniqueID:
   122  		return (*globalUniqueIDProvider).UniqueID(nil)
   123  	case uniqueid.CtxGlobalUniqueIDProvider:
   124  		return &globalUniqueIDProvider{}
   125  	case uniqueid.CtxInotifyCookie:
   126  		return lastInotifyCookie.Add(1)
   127  	case ktime.CtxRealtimeClock:
   128  		return &hostClock{}
   129  	default:
   130  		if val, ok := t.otherValues[key]; ok {
   131  			return val
   132  		}
   133  		return t.Context.Value(key)
   134  	}
   135  }
   136  
   137  // RootContext returns a Context that may be used in tests that need root
   138  // credentials. Uses ptrace as the platform.Platform.
   139  func RootContext(tb testing.TB) context.Context {
   140  	return auth.ContextWithCredentials(Context(tb), auth.NewRootCredentials(auth.NewRootUserNamespace()))
   141  }
   142  
   143  // WithLimitSet returns a copy of ctx carrying l.
   144  func WithLimitSet(ctx context.Context, l *limits.LimitSet) context.Context {
   145  	return limitContext{ctx, l}
   146  }
   147  
   148  type limitContext struct {
   149  	context.Context
   150  	l *limits.LimitSet
   151  }
   152  
   153  // Value implements context.Context.
   154  func (lc limitContext) Value(key any) any {
   155  	switch key {
   156  	case limits.CtxLimits:
   157  		return lc.l
   158  	default:
   159  		return lc.Context.Value(key)
   160  	}
   161  }