github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/sentry/kernel/vdso.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  	"context"
    19  	"fmt"
    20  
    21  	"github.com/metacubex/gvisor/pkg/hostarch"
    22  	"github.com/metacubex/gvisor/pkg/safemem"
    23  	"github.com/metacubex/gvisor/pkg/sentry/memmap"
    24  	"github.com/metacubex/gvisor/pkg/sentry/pgalloc"
    25  )
    26  
    27  // vdsoParams are the parameters exposed to the VDSO.
    28  //
    29  // They are exposed to the VDSO via a parameter page managed by VDSOParamPage,
    30  // which also includes a sequence counter.
    31  //
    32  // +marshal
    33  type vdsoParams struct {
    34  	monotonicReady      uint64
    35  	monotonicBaseCycles int64
    36  	monotonicBaseRef    int64
    37  	monotonicFrequency  uint64
    38  
    39  	realtimeReady      uint64
    40  	realtimeBaseCycles int64
    41  	realtimeBaseRef    int64
    42  	realtimeFrequency  uint64
    43  }
    44  
    45  // VDSOParamPage manages a VDSO parameter page.
    46  //
    47  // Its memory layout looks like:
    48  //
    49  //	type page struct {
    50  //		// seq is a sequence counter that protects the fields below.
    51  //		seq uint64
    52  //		vdsoParams
    53  //	}
    54  //
    55  // Everything in the struct is 8 bytes for easy alignment.
    56  //
    57  // It must be kept in sync with params in vdso/vdso_time.cc.
    58  //
    59  // +stateify savable
    60  type VDSOParamPage struct {
    61  	// The parameter page is fr, allocated from mf.
    62  	mf *pgalloc.MemoryFile `state:"nosave"`
    63  	fr memmap.FileRange
    64  
    65  	// seq is the current sequence count written to the page.
    66  	//
    67  	// A write is in progress if bit 1 of the counter is set.
    68  	//
    69  	// Timekeeper's updater goroutine may call Write before equality is
    70  	// checked in state_test_util tests, causing this field to change across
    71  	// save / restore.
    72  	seq uint64
    73  
    74  	// copyScratchBuffer is a temporary buffer used to marshal the params before
    75  	// copying it to the real parameter page. The parameter page is typically
    76  	// updated at a moderate frequency of ~O(seconds) throughout the lifetime of
    77  	// the sentry, so reusing this buffer is a good tradeoff between memory
    78  	// usage and the cost of allocation.
    79  	copyScratchBuffer []byte
    80  }
    81  
    82  // afterLoad is invoked by stateify.
    83  func (v *VDSOParamPage) afterLoad(ctx context.Context) {
    84  	v.mf = pgalloc.MemoryFileFromContext(ctx)
    85  }
    86  
    87  // NewVDSOParamPage returns a VDSOParamPage.
    88  //
    89  // Preconditions:
    90  //   - fr is a single page allocated from mf. VDSOParamPage does not take
    91  //     ownership of fr; it must remain allocated for the lifetime of the
    92  //     VDSOParamPage.
    93  //   - VDSOParamPage must be the only writer to fr.
    94  //   - mf.MapInternal(fr) must return a single safemem.Block.
    95  func NewVDSOParamPage(mf *pgalloc.MemoryFile, fr memmap.FileRange) *VDSOParamPage {
    96  	return &VDSOParamPage{
    97  		mf:                mf,
    98  		fr:                fr,
    99  		copyScratchBuffer: make([]byte, (*vdsoParams)(nil).SizeBytes()),
   100  	}
   101  }
   102  
   103  // access returns a mapping of the param page.
   104  func (v *VDSOParamPage) access() (safemem.Block, error) {
   105  	bs, err := v.mf.MapInternal(v.fr, hostarch.ReadWrite)
   106  	if err != nil {
   107  		return safemem.Block{}, err
   108  	}
   109  	if bs.NumBlocks() != 1 {
   110  		panic(fmt.Sprintf("Multiple blocks (%d) in VDSO param BlockSeq", bs.NumBlocks()))
   111  	}
   112  	return bs.Head(), nil
   113  }
   114  
   115  // incrementSeq increments the sequence counter in the param page.
   116  func (v *VDSOParamPage) incrementSeq(paramPage safemem.Block) error {
   117  	next := v.seq + 1
   118  	old, err := safemem.SwapUint64(paramPage, next)
   119  	if err != nil {
   120  		return err
   121  	}
   122  
   123  	if old != v.seq {
   124  		return fmt.Errorf("unexpected VDSOParamPage seq value: got %d expected %d; application may hang or get incorrect time from the VDSO", old, v.seq)
   125  	}
   126  
   127  	v.seq = next
   128  	return nil
   129  }
   130  
   131  // Write updates the VDSO parameters.
   132  //
   133  // Write starts a write block, calls f to get the new parameters, writes
   134  // out the new parameters, then ends the write block.
   135  func (v *VDSOParamPage) Write(f func() vdsoParams) error {
   136  	paramPage, err := v.access()
   137  	if err != nil {
   138  		return err
   139  	}
   140  
   141  	// Write begin.
   142  	next := v.seq + 1
   143  	if next%2 != 1 {
   144  		panic("Out-of-order sequence count")
   145  	}
   146  
   147  	err = v.incrementSeq(paramPage)
   148  	if err != nil {
   149  		return err
   150  	}
   151  
   152  	// Get the new params.
   153  	p := f()
   154  	buf := v.copyScratchBuffer[:p.SizeBytes()]
   155  	p.MarshalUnsafe(buf)
   156  
   157  	// Skip the sequence counter.
   158  	if _, err := safemem.Copy(paramPage.DropFirst(8), safemem.BlockFromSafeSlice(buf)); err != nil {
   159  		panic(fmt.Sprintf("Unable to get set VDSO parameters: %v", err))
   160  	}
   161  
   162  	// Write end.
   163  	return v.incrementSeq(paramPage)
   164  }