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