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 }