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  }