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