gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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 "gvisor.dev/gvisor/pkg/hostarch" 22 "gvisor.dev/gvisor/pkg/safemem" 23 "gvisor.dev/gvisor/pkg/sentry/memmap" 24 "gvisor.dev/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 }