github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/mm/mm_test.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 mm 16 17 import ( 18 "testing" 19 20 "github.com/SagerNet/gvisor/pkg/context" 21 "github.com/SagerNet/gvisor/pkg/errors/linuxerr" 22 "github.com/SagerNet/gvisor/pkg/hostarch" 23 "github.com/SagerNet/gvisor/pkg/sentry/arch" 24 "github.com/SagerNet/gvisor/pkg/sentry/contexttest" 25 "github.com/SagerNet/gvisor/pkg/sentry/limits" 26 "github.com/SagerNet/gvisor/pkg/sentry/memmap" 27 "github.com/SagerNet/gvisor/pkg/sentry/pgalloc" 28 "github.com/SagerNet/gvisor/pkg/sentry/platform" 29 "github.com/SagerNet/gvisor/pkg/usermem" 30 ) 31 32 func testMemoryManager(ctx context.Context) *MemoryManager { 33 p := platform.FromContext(ctx) 34 mfp := pgalloc.MemoryFileProviderFromContext(ctx) 35 mm := NewMemoryManager(p, mfp, false) 36 mm.layout = arch.MmapLayout{ 37 MinAddr: p.MinUserAddress(), 38 MaxAddr: p.MaxUserAddress(), 39 BottomUpBase: p.MinUserAddress(), 40 TopDownBase: p.MaxUserAddress(), 41 } 42 return mm 43 } 44 45 func (mm *MemoryManager) realUsageAS() uint64 { 46 return uint64(mm.vmas.Span()) 47 } 48 49 func TestUsageASUpdates(t *testing.T) { 50 ctx := contexttest.Context(t) 51 mm := testMemoryManager(ctx) 52 defer mm.DecUsers(ctx) 53 54 addr, err := mm.MMap(ctx, memmap.MMapOpts{ 55 Length: 2 * hostarch.PageSize, 56 Private: true, 57 }) 58 if err != nil { 59 t.Fatalf("MMap got err %v want nil", err) 60 } 61 realUsage := mm.realUsageAS() 62 if mm.usageAS != realUsage { 63 t.Fatalf("usageAS believes %v bytes are mapped; %v bytes are actually mapped", mm.usageAS, realUsage) 64 } 65 66 mm.MUnmap(ctx, addr, hostarch.PageSize) 67 realUsage = mm.realUsageAS() 68 if mm.usageAS != realUsage { 69 t.Fatalf("usageAS believes %v bytes are mapped; %v bytes are actually mapped", mm.usageAS, realUsage) 70 } 71 } 72 73 func (mm *MemoryManager) realDataAS() uint64 { 74 var sz uint64 75 for seg := mm.vmas.FirstSegment(); seg.Ok(); seg = seg.NextSegment() { 76 vma := seg.Value() 77 if vma.isPrivateDataLocked() { 78 sz += uint64(seg.Range().Length()) 79 } 80 } 81 return sz 82 } 83 84 func TestDataASUpdates(t *testing.T) { 85 ctx := contexttest.Context(t) 86 mm := testMemoryManager(ctx) 87 defer mm.DecUsers(ctx) 88 89 addr, err := mm.MMap(ctx, memmap.MMapOpts{ 90 Length: 3 * hostarch.PageSize, 91 Private: true, 92 Perms: hostarch.Write, 93 MaxPerms: hostarch.AnyAccess, 94 }) 95 if err != nil { 96 t.Fatalf("MMap got err %v want nil", err) 97 } 98 if mm.dataAS == 0 { 99 t.Fatalf("dataAS is 0, wanted not 0") 100 } 101 realDataAS := mm.realDataAS() 102 if mm.dataAS != realDataAS { 103 t.Fatalf("dataAS believes %v bytes are mapped; %v bytes are actually mapped", mm.dataAS, realDataAS) 104 } 105 106 mm.MUnmap(ctx, addr, hostarch.PageSize) 107 realDataAS = mm.realDataAS() 108 if mm.dataAS != realDataAS { 109 t.Fatalf("dataAS believes %v bytes are mapped; %v bytes are actually mapped", mm.dataAS, realDataAS) 110 } 111 112 mm.MProtect(addr+hostarch.PageSize, hostarch.PageSize, hostarch.Read, false) 113 realDataAS = mm.realDataAS() 114 if mm.dataAS != realDataAS { 115 t.Fatalf("dataAS believes %v bytes are mapped; %v bytes are actually mapped", mm.dataAS, realDataAS) 116 } 117 118 mm.MRemap(ctx, addr+2*hostarch.PageSize, hostarch.PageSize, 2*hostarch.PageSize, MRemapOpts{ 119 Move: MRemapMayMove, 120 }) 121 realDataAS = mm.realDataAS() 122 if mm.dataAS != realDataAS { 123 t.Fatalf("dataAS believes %v bytes are mapped; %v bytes are actually mapped", mm.dataAS, realDataAS) 124 } 125 } 126 127 func TestBrkDataLimitUpdates(t *testing.T) { 128 limitSet := limits.NewLimitSet() 129 limitSet.Set(limits.Data, limits.Limit{}, true /* privileged */) // zero RLIMIT_DATA 130 131 ctx := contexttest.WithLimitSet(contexttest.Context(t), limitSet) 132 mm := testMemoryManager(ctx) 133 defer mm.DecUsers(ctx) 134 135 // Try to extend the brk by one page and expect doing so to fail. 136 oldBrk, _ := mm.Brk(ctx, 0) 137 if newBrk, _ := mm.Brk(ctx, oldBrk+hostarch.PageSize); newBrk != oldBrk { 138 t.Errorf("brk() increased data segment above RLIMIT_DATA (old brk = %#x, new brk = %#x", oldBrk, newBrk) 139 } 140 } 141 142 // TestIOAfterUnmap ensures that IO fails after unmap. 143 func TestIOAfterUnmap(t *testing.T) { 144 ctx := contexttest.Context(t) 145 mm := testMemoryManager(ctx) 146 defer mm.DecUsers(ctx) 147 148 addr, err := mm.MMap(ctx, memmap.MMapOpts{ 149 Length: hostarch.PageSize, 150 Private: true, 151 Perms: hostarch.Read, 152 MaxPerms: hostarch.AnyAccess, 153 }) 154 if err != nil { 155 t.Fatalf("MMap got err %v want nil", err) 156 } 157 158 // IO works before munmap. 159 b := make([]byte, 1) 160 n, err := mm.CopyIn(ctx, addr, b, usermem.IOOpts{}) 161 if err != nil { 162 t.Errorf("CopyIn got err %v want nil", err) 163 } 164 if n != 1 { 165 t.Errorf("CopyIn got %d want 1", n) 166 } 167 168 err = mm.MUnmap(ctx, addr, hostarch.PageSize) 169 if err != nil { 170 t.Fatalf("MUnmap got err %v want nil", err) 171 } 172 173 n, err = mm.CopyIn(ctx, addr, b, usermem.IOOpts{}) 174 if !linuxerr.Equals(linuxerr.EFAULT, err) { 175 t.Errorf("CopyIn got err %v want EFAULT", err) 176 } 177 if n != 0 { 178 t.Errorf("CopyIn got %d want 0", n) 179 } 180 } 181 182 // TestIOAfterMProtect tests IO interaction with mprotect permissions. 183 func TestIOAfterMProtect(t *testing.T) { 184 ctx := contexttest.Context(t) 185 mm := testMemoryManager(ctx) 186 defer mm.DecUsers(ctx) 187 188 addr, err := mm.MMap(ctx, memmap.MMapOpts{ 189 Length: hostarch.PageSize, 190 Private: true, 191 Perms: hostarch.ReadWrite, 192 MaxPerms: hostarch.AnyAccess, 193 }) 194 if err != nil { 195 t.Fatalf("MMap got err %v want nil", err) 196 } 197 198 // Writing works before mprotect. 199 b := make([]byte, 1) 200 n, err := mm.CopyOut(ctx, addr, b, usermem.IOOpts{}) 201 if err != nil { 202 t.Errorf("CopyOut got err %v want nil", err) 203 } 204 if n != 1 { 205 t.Errorf("CopyOut got %d want 1", n) 206 } 207 208 err = mm.MProtect(addr, hostarch.PageSize, hostarch.Read, false) 209 if err != nil { 210 t.Errorf("MProtect got err %v want nil", err) 211 } 212 213 // Without IgnorePermissions, CopyOut should no longer succeed. 214 n, err = mm.CopyOut(ctx, addr, b, usermem.IOOpts{}) 215 if !linuxerr.Equals(linuxerr.EFAULT, err) { 216 t.Errorf("CopyOut got err %v want EFAULT", err) 217 } 218 if n != 0 { 219 t.Errorf("CopyOut got %d want 0", n) 220 } 221 222 // With IgnorePermissions, CopyOut should succeed despite mprotect. 223 n, err = mm.CopyOut(ctx, addr, b, usermem.IOOpts{ 224 IgnorePermissions: true, 225 }) 226 if err != nil { 227 t.Errorf("CopyOut got err %v want nil", err) 228 } 229 if n != 1 { 230 t.Errorf("CopyOut got %d want 1", n) 231 } 232 } 233 234 // TestAIOPrepareAfterDestroy tests that AIOContext should not be able to be 235 // prepared after destruction. 236 func TestAIOPrepareAfterDestroy(t *testing.T) { 237 ctx := contexttest.Context(t) 238 mm := testMemoryManager(ctx) 239 defer mm.DecUsers(ctx) 240 241 id, err := mm.NewAIOContext(ctx, 1) 242 if err != nil { 243 t.Fatalf("mm.NewAIOContext got err %v want nil", err) 244 } 245 aioCtx, ok := mm.LookupAIOContext(ctx, id) 246 if !ok { 247 t.Fatalf("AIOContext not found") 248 } 249 mm.DestroyAIOContext(ctx, id) 250 251 // Prepare should fail because aioCtx should be destroyed. 252 if err := aioCtx.Prepare(); !linuxerr.Equals(linuxerr.EINVAL, err) { 253 t.Errorf("aioCtx.Prepare got err %v want nil", err) 254 } else if err == nil { 255 aioCtx.CancelPendingRequest() 256 } 257 } 258 259 // TestAIOLookupAfterDestroy tests that AIOContext should not be able to be 260 // looked up after memory manager is destroyed. 261 func TestAIOLookupAfterDestroy(t *testing.T) { 262 ctx := contexttest.Context(t) 263 mm := testMemoryManager(ctx) 264 265 id, err := mm.NewAIOContext(ctx, 1) 266 if err != nil { 267 mm.DecUsers(ctx) 268 t.Fatalf("mm.NewAIOContext got err %v want nil", err) 269 } 270 mm.DecUsers(ctx) // This destroys the AIOContext manager. 271 272 if _, ok := mm.LookupAIOContext(ctx, id); ok { 273 t.Errorf("AIOContext found even after AIOContext manager is destroyed") 274 } 275 }