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 }