2019-04-29 21:25:05 +00:00
|
|
|
// Copyright 2018 The gVisor Authors.
|
2018-04-27 17:37:02 +00:00
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package mm
|
|
|
|
|
|
|
|
import (
|
|
|
|
"testing"
|
|
|
|
|
2019-06-13 23:49:09 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/sentry/arch"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/context"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/context/contexttest"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/limits"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/memmap"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/pgalloc"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/platform"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/usermem"
|
|
|
|
"gvisor.dev/gvisor/pkg/syserror"
|
2018-04-27 17:37:02 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func testMemoryManager(ctx context.Context) *MemoryManager {
|
|
|
|
p := platform.FromContext(ctx)
|
2019-03-14 15:11:36 +00:00
|
|
|
mfp := pgalloc.MemoryFileProviderFromContext(ctx)
|
|
|
|
mm := NewMemoryManager(p, mfp)
|
2018-04-27 17:37:02 +00:00
|
|
|
mm.layout = arch.MmapLayout{
|
|
|
|
MinAddr: p.MinUserAddress(),
|
|
|
|
MaxAddr: p.MaxUserAddress(),
|
|
|
|
BottomUpBase: p.MinUserAddress(),
|
|
|
|
TopDownBase: p.MaxUserAddress(),
|
|
|
|
}
|
|
|
|
return mm
|
|
|
|
}
|
|
|
|
|
|
|
|
func (mm *MemoryManager) realUsageAS() uint64 {
|
|
|
|
return uint64(mm.vmas.Span())
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestUsageASUpdates(t *testing.T) {
|
|
|
|
ctx := contexttest.Context(t)
|
|
|
|
mm := testMemoryManager(ctx)
|
|
|
|
defer mm.DecUsers(ctx)
|
|
|
|
|
|
|
|
addr, err := mm.MMap(ctx, memmap.MMapOpts{
|
|
|
|
Length: 2 * usermem.PageSize,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("MMap got err %v want nil", err)
|
|
|
|
}
|
|
|
|
realUsage := mm.realUsageAS()
|
|
|
|
if mm.usageAS != realUsage {
|
|
|
|
t.Fatalf("usageAS believes %v bytes are mapped; %v bytes are actually mapped", mm.usageAS, realUsage)
|
|
|
|
}
|
|
|
|
|
|
|
|
mm.MUnmap(ctx, addr, usermem.PageSize)
|
|
|
|
realUsage = mm.realUsageAS()
|
|
|
|
if mm.usageAS != realUsage {
|
|
|
|
t.Fatalf("usageAS believes %v bytes are mapped; %v bytes are actually mapped", mm.usageAS, realUsage)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-29 23:48:19 +00:00
|
|
|
func (mm *MemoryManager) realDataAS() uint64 {
|
|
|
|
var sz uint64
|
|
|
|
for seg := mm.vmas.FirstSegment(); seg.Ok(); seg = seg.NextSegment() {
|
|
|
|
vma := seg.Value()
|
|
|
|
if vma.isPrivateDataLocked() {
|
|
|
|
sz += uint64(seg.Range().Length())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return sz
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDataASUpdates(t *testing.T) {
|
|
|
|
ctx := contexttest.Context(t)
|
|
|
|
mm := testMemoryManager(ctx)
|
|
|
|
defer mm.DecUsers(ctx)
|
|
|
|
|
|
|
|
addr, err := mm.MMap(ctx, memmap.MMapOpts{
|
|
|
|
Length: 3 * usermem.PageSize,
|
|
|
|
Private: true,
|
|
|
|
Perms: usermem.Write,
|
|
|
|
MaxPerms: usermem.AnyAccess,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("MMap got err %v want nil", err)
|
|
|
|
}
|
|
|
|
if mm.dataAS == 0 {
|
|
|
|
t.Fatalf("dataAS is 0, wanted not 0")
|
|
|
|
}
|
|
|
|
realDataAS := mm.realDataAS()
|
|
|
|
if mm.dataAS != realDataAS {
|
|
|
|
t.Fatalf("dataAS believes %v bytes are mapped; %v bytes are actually mapped", mm.dataAS, realDataAS)
|
|
|
|
}
|
|
|
|
|
|
|
|
mm.MUnmap(ctx, addr, usermem.PageSize)
|
|
|
|
realDataAS = mm.realDataAS()
|
|
|
|
if mm.dataAS != realDataAS {
|
|
|
|
t.Fatalf("dataAS believes %v bytes are mapped; %v bytes are actually mapped", mm.dataAS, realDataAS)
|
|
|
|
}
|
|
|
|
|
|
|
|
mm.MProtect(addr+usermem.PageSize, usermem.PageSize, usermem.Read, false)
|
|
|
|
realDataAS = mm.realDataAS()
|
|
|
|
if mm.dataAS != realDataAS {
|
|
|
|
t.Fatalf("dataAS believes %v bytes are mapped; %v bytes are actually mapped", mm.dataAS, realDataAS)
|
|
|
|
}
|
|
|
|
|
|
|
|
mm.MRemap(ctx, addr+2*usermem.PageSize, usermem.PageSize, 2*usermem.PageSize, MRemapOpts{
|
|
|
|
Move: MRemapMayMove,
|
|
|
|
})
|
|
|
|
realDataAS = mm.realDataAS()
|
|
|
|
if mm.dataAS != realDataAS {
|
|
|
|
t.Fatalf("dataAS believes %v bytes are mapped; %v bytes are actually mapped", mm.dataAS, realDataAS)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
func TestBrkDataLimitUpdates(t *testing.T) {
|
|
|
|
limitSet := limits.NewLimitSet()
|
2019-04-10 19:35:43 +00:00
|
|
|
limitSet.Set(limits.Data, limits.Limit{}, true /* privileged */) // zero RLIMIT_DATA
|
2018-04-27 17:37:02 +00:00
|
|
|
|
|
|
|
ctx := contexttest.WithLimitSet(contexttest.Context(t), limitSet)
|
|
|
|
mm := testMemoryManager(ctx)
|
|
|
|
defer mm.DecUsers(ctx)
|
|
|
|
|
|
|
|
// Try to extend the brk by one page and expect doing so to fail.
|
|
|
|
oldBrk, _ := mm.Brk(ctx, 0)
|
|
|
|
if newBrk, _ := mm.Brk(ctx, oldBrk+usermem.PageSize); newBrk != oldBrk {
|
|
|
|
t.Errorf("brk() increased data segment above RLIMIT_DATA (old brk = %#x, new brk = %#x", oldBrk, newBrk)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestIOAfterUnmap ensures that IO fails after unmap.
|
|
|
|
func TestIOAfterUnmap(t *testing.T) {
|
|
|
|
ctx := contexttest.Context(t)
|
|
|
|
mm := testMemoryManager(ctx)
|
|
|
|
defer mm.DecUsers(ctx)
|
|
|
|
|
|
|
|
addr, err := mm.MMap(ctx, memmap.MMapOpts{
|
|
|
|
Length: usermem.PageSize,
|
|
|
|
Private: true,
|
|
|
|
Perms: usermem.Read,
|
|
|
|
MaxPerms: usermem.AnyAccess,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("MMap got err %v want nil", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// IO works before munmap.
|
|
|
|
b := make([]byte, 1)
|
|
|
|
n, err := mm.CopyIn(ctx, addr, b, usermem.IOOpts{})
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("CopyIn got err %v want nil", err)
|
|
|
|
}
|
|
|
|
if n != 1 {
|
|
|
|
t.Errorf("CopyIn got %d want 1", n)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = mm.MUnmap(ctx, addr, usermem.PageSize)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("MUnmap got err %v want nil", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
n, err = mm.CopyIn(ctx, addr, b, usermem.IOOpts{})
|
|
|
|
if err != syserror.EFAULT {
|
|
|
|
t.Errorf("CopyIn got err %v want EFAULT", err)
|
|
|
|
}
|
|
|
|
if n != 0 {
|
|
|
|
t.Errorf("CopyIn got %d want 0", n)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestIOAfterMProtect tests IO interaction with mprotect permissions.
|
|
|
|
func TestIOAfterMProtect(t *testing.T) {
|
|
|
|
ctx := contexttest.Context(t)
|
|
|
|
mm := testMemoryManager(ctx)
|
|
|
|
defer mm.DecUsers(ctx)
|
|
|
|
|
|
|
|
addr, err := mm.MMap(ctx, memmap.MMapOpts{
|
|
|
|
Length: usermem.PageSize,
|
|
|
|
Private: true,
|
|
|
|
Perms: usermem.ReadWrite,
|
|
|
|
MaxPerms: usermem.AnyAccess,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("MMap got err %v want nil", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Writing works before mprotect.
|
|
|
|
b := make([]byte, 1)
|
|
|
|
n, err := mm.CopyOut(ctx, addr, b, usermem.IOOpts{})
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("CopyOut got err %v want nil", err)
|
|
|
|
}
|
|
|
|
if n != 1 {
|
|
|
|
t.Errorf("CopyOut got %d want 1", n)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = mm.MProtect(addr, usermem.PageSize, usermem.Read, false)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("MProtect got err %v want nil", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Without IgnorePermissions, CopyOut should no longer succeed.
|
|
|
|
n, err = mm.CopyOut(ctx, addr, b, usermem.IOOpts{})
|
|
|
|
if err != syserror.EFAULT {
|
|
|
|
t.Errorf("CopyOut got err %v want EFAULT", err)
|
|
|
|
}
|
|
|
|
if n != 0 {
|
|
|
|
t.Errorf("CopyOut got %d want 0", n)
|
|
|
|
}
|
|
|
|
|
|
|
|
// With IgnorePermissions, CopyOut should succeed despite mprotect.
|
|
|
|
n, err = mm.CopyOut(ctx, addr, b, usermem.IOOpts{
|
|
|
|
IgnorePermissions: true,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("CopyOut got err %v want nil", err)
|
|
|
|
}
|
|
|
|
if n != 1 {
|
|
|
|
t.Errorf("CopyOut got %d want 1", n)
|
|
|
|
}
|
|
|
|
}
|