gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/sentry/kernel/timekeeper_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 kernel
    16  
    17  import (
    18  	"testing"
    19  
    20  	"gvisor.dev/gvisor/pkg/errors/linuxerr"
    21  	"gvisor.dev/gvisor/pkg/hostarch"
    22  	"gvisor.dev/gvisor/pkg/sentry/contexttest"
    23  	"gvisor.dev/gvisor/pkg/sentry/pgalloc"
    24  	sentrytime "gvisor.dev/gvisor/pkg/sentry/time"
    25  	"gvisor.dev/gvisor/pkg/sentry/usage"
    26  )
    27  
    28  // mockClocks is a sentrytime.Clocks that simply returns the times in the
    29  // struct.
    30  type mockClocks struct {
    31  	monotonic int64
    32  	realtime  int64
    33  }
    34  
    35  // Update implements sentrytime.Clocks.Update. It does nothing.
    36  func (*mockClocks) Update() (monotonicParams sentrytime.Parameters, monotonicOk bool, realtimeParam sentrytime.Parameters, realtimeOk bool) {
    37  	return
    38  }
    39  
    40  // Update implements sentrytime.Clocks.GetTime.
    41  func (c *mockClocks) GetTime(id sentrytime.ClockID) (int64, error) {
    42  	switch id {
    43  	case sentrytime.Monotonic:
    44  		return c.monotonic, nil
    45  	case sentrytime.Realtime:
    46  		return c.realtime, nil
    47  	default:
    48  		return 0, linuxerr.EINVAL
    49  	}
    50  }
    51  
    52  // stateTestClocklessTimekeeper returns a test Timekeeper which has not had
    53  // SetClocks called.
    54  func stateTestClocklessTimekeeper(tb testing.TB) *Timekeeper {
    55  	ctx := contexttest.Context(tb)
    56  	mf := pgalloc.MemoryFileFromContext(ctx)
    57  	fr, err := mf.Allocate(hostarch.PageSize, pgalloc.AllocOpts{Kind: usage.Anonymous})
    58  	if err != nil {
    59  		tb.Fatalf("failed to allocate memory: %v", err)
    60  	}
    61  	return &Timekeeper{
    62  		params: NewVDSOParamPage(mf, fr),
    63  	}
    64  }
    65  
    66  func stateTestTimekeeper(tb testing.TB) *Timekeeper {
    67  	t := stateTestClocklessTimekeeper(tb)
    68  	t.SetClocks(sentrytime.NewCalibratedClocks())
    69  	return t
    70  }
    71  
    72  // TestTimekeeperMonotonicZero tests that monotonic time starts at zero.
    73  func TestTimekeeperMonotonicZero(t *testing.T) {
    74  	c := &mockClocks{
    75  		monotonic: 100000,
    76  	}
    77  
    78  	tk := stateTestClocklessTimekeeper(t)
    79  	tk.SetClocks(c)
    80  	defer tk.Destroy()
    81  
    82  	now, err := tk.GetTime(sentrytime.Monotonic)
    83  	if err != nil {
    84  		t.Errorf("GetTime err got %v want nil", err)
    85  	}
    86  	if now != 0 {
    87  		t.Errorf("GetTime got %d want 0", now)
    88  	}
    89  
    90  	c.monotonic += 10
    91  
    92  	now, err = tk.GetTime(sentrytime.Monotonic)
    93  	if err != nil {
    94  		t.Errorf("GetTime err got %v want nil", err)
    95  	}
    96  	if now != 10 {
    97  		t.Errorf("GetTime got %d want 10", now)
    98  	}
    99  }
   100  
   101  // TestTimekeeperMonotonicJumpForward tests that monotonic time jumps forward
   102  // after restore.
   103  func TestTimekeeperMonotonicForward(t *testing.T) {
   104  	c := &mockClocks{
   105  		monotonic: 900000,
   106  		realtime:  600000,
   107  	}
   108  
   109  	tk := stateTestClocklessTimekeeper(t)
   110  	tk.restored = make(chan struct{})
   111  	tk.saveMonotonic = 100000
   112  	tk.saveRealtime = 400000
   113  	tk.SetClocks(c)
   114  	defer tk.Destroy()
   115  
   116  	// The monotonic clock should jump ahead by 200000 to 300000.
   117  	//
   118  	// The new system monotonic time (900000) is irrelevant to what the app
   119  	// sees.
   120  	now, err := tk.GetTime(sentrytime.Monotonic)
   121  	if err != nil {
   122  		t.Errorf("GetTime err got %v want nil", err)
   123  	}
   124  	if now != 300000 {
   125  		t.Errorf("GetTime got %d want 300000", now)
   126  	}
   127  }
   128  
   129  // TestTimekeeperMonotonicJumpBackwards tests that monotonic time does not jump
   130  // backwards when realtime goes backwards.
   131  func TestTimekeeperMonotonicJumpBackwards(t *testing.T) {
   132  	c := &mockClocks{
   133  		monotonic: 900000,
   134  		realtime:  400000,
   135  	}
   136  
   137  	tk := stateTestClocklessTimekeeper(t)
   138  	tk.restored = make(chan struct{})
   139  	tk.saveMonotonic = 100000
   140  	tk.saveRealtime = 600000
   141  	tk.SetClocks(c)
   142  	defer tk.Destroy()
   143  
   144  	// The monotonic clock should remain at 100000.
   145  	//
   146  	// The new system monotonic time (900000) is irrelevant to what the app
   147  	// sees and we don't want to jump the monotonic clock backwards like
   148  	// realtime did.
   149  	now, err := tk.GetTime(sentrytime.Monotonic)
   150  	if err != nil {
   151  		t.Errorf("GetTime err got %v want nil", err)
   152  	}
   153  	if now != 100000 {
   154  		t.Errorf("GetTime got %d want 100000", now)
   155  	}
   156  }