github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/ring0/pagetables/pagetables_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 pagetables
    16  
    17  import (
    18  	"github.com/SagerNet/gvisor/pkg/hostarch"
    19  	"testing"
    20  )
    21  
    22  type mapping struct {
    23  	start  uintptr
    24  	length uintptr
    25  	addr   uintptr
    26  	opts   MapOpts
    27  }
    28  
    29  type checkVisitor struct {
    30  	expected []mapping // Input.
    31  	current  int       // Temporary.
    32  	found    []mapping // Output.
    33  	failed   string    // Output.
    34  }
    35  
    36  func (v *checkVisitor) visit(start uintptr, pte *PTE, align uintptr) bool {
    37  	v.found = append(v.found, mapping{
    38  		start:  start,
    39  		length: align + 1,
    40  		addr:   pte.Address(),
    41  		opts:   pte.Opts(),
    42  	})
    43  	if v.failed != "" {
    44  		// Don't keep looking for errors.
    45  		return false
    46  	}
    47  
    48  	if v.current >= len(v.expected) {
    49  		v.failed = "more mappings than expected"
    50  	} else if v.expected[v.current].start != start {
    51  		v.failed = "start didn't match expected"
    52  	} else if v.expected[v.current].length != (align + 1) {
    53  		v.failed = "end didn't match expected"
    54  	} else if v.expected[v.current].addr != pte.Address() {
    55  		v.failed = "address didn't match expected"
    56  	} else if v.expected[v.current].opts != pte.Opts() {
    57  		v.failed = "opts didn't match"
    58  	}
    59  	v.current++
    60  	return true
    61  }
    62  
    63  func (*checkVisitor) requiresAlloc() bool { return false }
    64  
    65  func (*checkVisitor) requiresSplit() bool { return false }
    66  
    67  func checkMappings(t *testing.T, pt *PageTables, m []mapping) {
    68  	// Iterate over all the mappings.
    69  	w := checkWalker{
    70  		pageTables: pt,
    71  		visitor: checkVisitor{
    72  			expected: m,
    73  		},
    74  	}
    75  	w.iterateRange(0, ^uintptr(0))
    76  
    77  	// Were we expected additional mappings?
    78  	if w.visitor.failed == "" && w.visitor.current != len(w.visitor.expected) {
    79  		w.visitor.failed = "insufficient mappings found"
    80  	}
    81  
    82  	// Emit a meaningful error message on failure.
    83  	if w.visitor.failed != "" {
    84  		t.Errorf("%s; got %#v, wanted %#v", w.visitor.failed, w.visitor.found, w.visitor.expected)
    85  	}
    86  }
    87  
    88  func TestUnmap(t *testing.T) {
    89  	pt := New(NewRuntimeAllocator())
    90  
    91  	// Map and unmap one entry.
    92  	pt.Map(0x400000, pteSize, MapOpts{AccessType: hostarch.ReadWrite}, pteSize*42)
    93  	pt.Unmap(0x400000, pteSize)
    94  
    95  	checkMappings(t, pt, nil)
    96  }
    97  
    98  func TestReadOnly(t *testing.T) {
    99  	pt := New(NewRuntimeAllocator())
   100  
   101  	// Map one entry.
   102  	pt.Map(0x400000, pteSize, MapOpts{AccessType: hostarch.Read}, pteSize*42)
   103  
   104  	checkMappings(t, pt, []mapping{
   105  		{0x400000, pteSize, pteSize * 42, MapOpts{AccessType: hostarch.Read}},
   106  	})
   107  }
   108  
   109  func TestReadWrite(t *testing.T) {
   110  	pt := New(NewRuntimeAllocator())
   111  
   112  	// Map one entry.
   113  	pt.Map(0x400000, pteSize, MapOpts{AccessType: hostarch.ReadWrite}, pteSize*42)
   114  
   115  	checkMappings(t, pt, []mapping{
   116  		{0x400000, pteSize, pteSize * 42, MapOpts{AccessType: hostarch.ReadWrite}},
   117  	})
   118  }
   119  
   120  func TestSerialEntries(t *testing.T) {
   121  	pt := New(NewRuntimeAllocator())
   122  
   123  	// Map two sequential entries.
   124  	pt.Map(0x400000, pteSize, MapOpts{AccessType: hostarch.ReadWrite}, pteSize*42)
   125  	pt.Map(0x401000, pteSize, MapOpts{AccessType: hostarch.ReadWrite}, pteSize*47)
   126  
   127  	checkMappings(t, pt, []mapping{
   128  		{0x400000, pteSize, pteSize * 42, MapOpts{AccessType: hostarch.ReadWrite}},
   129  		{0x401000, pteSize, pteSize * 47, MapOpts{AccessType: hostarch.ReadWrite}},
   130  	})
   131  }
   132  
   133  func TestSpanningEntries(t *testing.T) {
   134  	pt := New(NewRuntimeAllocator())
   135  
   136  	// Span a pgd with two pages.
   137  	pt.Map(0x00007efffffff000, 2*pteSize, MapOpts{AccessType: hostarch.Read}, pteSize*42)
   138  
   139  	checkMappings(t, pt, []mapping{
   140  		{0x00007efffffff000, pteSize, pteSize * 42, MapOpts{AccessType: hostarch.Read}},
   141  		{0x00007f0000000000, pteSize, pteSize * 43, MapOpts{AccessType: hostarch.Read}},
   142  	})
   143  }
   144  
   145  func TestSparseEntries(t *testing.T) {
   146  	pt := New(NewRuntimeAllocator())
   147  
   148  	// Map two entries in different pgds.
   149  	pt.Map(0x400000, pteSize, MapOpts{AccessType: hostarch.ReadWrite}, pteSize*42)
   150  	pt.Map(0x00007f0000000000, pteSize, MapOpts{AccessType: hostarch.Read}, pteSize*47)
   151  
   152  	checkMappings(t, pt, []mapping{
   153  		{0x400000, pteSize, pteSize * 42, MapOpts{AccessType: hostarch.ReadWrite}},
   154  		{0x00007f0000000000, pteSize, pteSize * 47, MapOpts{AccessType: hostarch.Read}},
   155  	})
   156  }