github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/sentry/arch/arch_arm64.go (about) 1 // Copyright 2020 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 //go:build arm64 16 // +build arm64 17 18 package arch 19 20 import ( 21 "fmt" 22 23 "golang.org/x/sys/unix" 24 "github.com/metacubex/gvisor/pkg/hostarch" 25 "github.com/metacubex/gvisor/pkg/marshal" 26 "github.com/metacubex/gvisor/pkg/marshal/primitive" 27 "github.com/metacubex/gvisor/pkg/rand" 28 "github.com/metacubex/gvisor/pkg/sentry/arch/fpu" 29 "github.com/metacubex/gvisor/pkg/sentry/limits" 30 ) 31 32 // Host specifies the host architecture. 33 const Host = ARM64 34 35 // These constants come directly from Linux. 36 const ( 37 // maxAddr64 is the maximum userspace address. It is TASK_SIZE in Linux 38 // for a 64-bit process. 39 maxAddr64 hostarch.Addr = (1 << 48) 40 41 // maxStackRand64 is the maximum randomization to apply to the stack. 42 // It is defined by arch/arm64/mm/mmap.c:(STACK_RND_MASK << PAGE_SHIFT) in Linux. 43 maxStackRand64 = 0x3ffff << 12 // 16 GB 44 45 // maxMmapRand64 is the maximum randomization to apply to the mmap 46 // layout. It is defined by arch/arm64/mm/mmap.c:arch_mmap_rnd in Linux. 47 maxMmapRand64 = (1 << 33) * hostarch.PageSize 48 49 // minGap64 is the minimum gap to leave at the top of the address space 50 // for the stack. It is defined by arch/arm64/mm/mmap.c:MIN_GAP in Linux. 51 minGap64 = (128 << 20) + maxStackRand64 52 53 // preferredPIELoadAddr is the standard Linux position-independent 54 // executable base load address. It is ELF_ET_DYN_BASE in Linux. 55 // 56 // The Platform {Min,Max}UserAddress() may preclude loading at this 57 // address. See other preferredFoo comments below. 58 preferredPIELoadAddr hostarch.Addr = maxAddr64 / 6 * 5 59 ) 60 61 var ( 62 // CPUIDInstruction doesn't exist on ARM64. 63 CPUIDInstruction = []byte{} 64 ) 65 66 // These constants are selected as heuristics to help make the Platform's 67 // potentially limited address space conform as closely to Linux as possible. 68 const ( 69 preferredTopDownAllocMin hostarch.Addr = 0x7e8000000000 70 preferredAllocationGap = 128 << 30 // 128 GB 71 preferredTopDownBaseMin = preferredTopDownAllocMin + preferredAllocationGap 72 73 // minMmapRand64 is the smallest we are willing to make the 74 // randomization to stay above preferredTopDownBaseMin. 75 minMmapRand64 = (1 << 18) * hostarch.PageSize 76 ) 77 78 // Context64 represents an ARM64 context. 79 // 80 // +stateify savable 81 type Context64 struct { 82 State 83 sigFPState []fpu.State // fpstate to be restored on sigreturn. 84 } 85 86 // Arch implements Context.Arch. 87 func (c *Context64) Arch() Arch { 88 return ARM64 89 } 90 91 func (c *Context64) copySigFPState() []fpu.State { 92 var sigfps []fpu.State 93 for _, s := range c.sigFPState { 94 sigfps = append(sigfps, s.Fork()) 95 } 96 return sigfps 97 } 98 99 // Fork returns an exact copy of this context. 100 func (c *Context64) Fork() *Context64 { 101 return &Context64{ 102 State: c.State.Fork(), 103 sigFPState: c.copySigFPState(), 104 } 105 } 106 107 // General purpose registers usage on Arm64: 108 // R0...R7: parameter/result registers. 109 // R8: indirect result location register. 110 // R9...R15: temporary rgisters. 111 // R16: the first intra-procedure-call scratch register. 112 // R17: the second intra-procedure-call scratch register. 113 // R18: the platform register. 114 // R19...R28: callee-saved registers. 115 // R29: the frame pointer. 116 // R30: the link register. 117 118 // Return returns the current syscall return value. 119 func (c *Context64) Return() uintptr { 120 return uintptr(c.Regs.Regs[0]) 121 } 122 123 // SetReturn sets the syscall return value. 124 func (c *Context64) SetReturn(value uintptr) { 125 c.Regs.Regs[0] = uint64(value) 126 } 127 128 // IP returns the current instruction pointer. 129 func (c *Context64) IP() uintptr { 130 return uintptr(c.Regs.Pc) 131 } 132 133 // SetIP sets the current instruction pointer. 134 func (c *Context64) SetIP(value uintptr) { 135 c.Regs.Pc = uint64(value) 136 } 137 138 // Stack returns the current stack pointer. 139 func (c *Context64) Stack() uintptr { 140 return uintptr(c.Regs.Sp) 141 } 142 143 // SetStack sets the current stack pointer. 144 func (c *Context64) SetStack(value uintptr) { 145 c.Regs.Sp = uint64(value) 146 } 147 148 // TLS returns the current TLS pointer. 149 func (c *Context64) TLS() uintptr { 150 return uintptr(c.Regs.TPIDR_EL0) 151 } 152 153 // SetTLS sets the current TLS pointer. Returns false if value is invalid. 154 func (c *Context64) SetTLS(value uintptr) bool { 155 if value >= uintptr(maxAddr64) { 156 return false 157 } 158 159 c.Regs.TPIDR_EL0 = uint64(value) 160 return true 161 } 162 163 // SetOldRSeqInterruptedIP implements Context.SetOldRSeqInterruptedIP. 164 func (c *Context64) SetOldRSeqInterruptedIP(value uintptr) { 165 c.Regs.Regs[3] = uint64(value) 166 } 167 168 // Native returns the native type for the given val. 169 func (c *Context64) Native(val uintptr) marshal.Marshallable { 170 v := primitive.Uint64(val) 171 return &v 172 } 173 174 // Value returns the generic val for the given native type. 175 func (c *Context64) Value(val marshal.Marshallable) uintptr { 176 return uintptr(*val.(*primitive.Uint64)) 177 } 178 179 // Width returns the byte width of this architecture. 180 func (c *Context64) Width() uint { 181 return 8 182 } 183 184 // mmapRand returns a random adjustment for randomizing an mmap layout. 185 func mmapRand(max uint64) hostarch.Addr { 186 return hostarch.Addr(rand.Int63n(int64(max))).RoundDown() 187 } 188 189 // NewMmapLayout implements Context.NewMmapLayout consistently with Linux. 190 func (c *Context64) NewMmapLayout(min, max hostarch.Addr, r *limits.LimitSet) (MmapLayout, error) { 191 min, ok := min.RoundUp() 192 if !ok { 193 return MmapLayout{}, unix.EINVAL 194 } 195 if max > maxAddr64 { 196 max = maxAddr64 197 } 198 max = max.RoundDown() 199 200 if min > max { 201 return MmapLayout{}, unix.EINVAL 202 } 203 204 stackSize := r.Get(limits.Stack) 205 206 // MAX_GAP in Linux. 207 maxGap := (max / 6) * 5 208 gap := hostarch.Addr(stackSize.Cur) 209 if gap < minGap64 { 210 gap = minGap64 211 } 212 if gap > maxGap { 213 gap = maxGap 214 } 215 defaultDir := MmapTopDown 216 if stackSize.Cur == limits.Infinity { 217 defaultDir = MmapBottomUp 218 } 219 220 topDownMin := max - gap - maxMmapRand64 221 maxRand := hostarch.Addr(maxMmapRand64) 222 if topDownMin < preferredTopDownBaseMin { 223 // Try to keep TopDownBase above preferredTopDownBaseMin by 224 // shrinking maxRand. 225 maxAdjust := maxRand - minMmapRand64 226 needAdjust := preferredTopDownBaseMin - topDownMin 227 if needAdjust <= maxAdjust { 228 maxRand -= needAdjust 229 } 230 } 231 232 rnd := mmapRand(uint64(maxRand)) 233 l := MmapLayout{ 234 MinAddr: min, 235 MaxAddr: max, 236 // TASK_UNMAPPED_BASE in Linux. 237 BottomUpBase: (max/3 + rnd).RoundDown(), 238 TopDownBase: (max - gap - rnd).RoundDown(), 239 DefaultDirection: defaultDir, 240 // We may have reduced the maximum randomization to keep 241 // TopDownBase above preferredTopDownBaseMin while maintaining 242 // our stack gap. Stack allocations must use that max 243 // randomization to avoiding eating into the gap. 244 MaxStackRand: uint64(maxRand), 245 } 246 247 // Final sanity check on the layout. 248 if !l.Valid() { 249 panic(fmt.Sprintf("Invalid MmapLayout: %+v", l)) 250 } 251 252 return l, nil 253 } 254 255 // PIELoadAddress implements Context.PIELoadAddress. 256 func (c *Context64) PIELoadAddress(l MmapLayout) hostarch.Addr { 257 base := preferredPIELoadAddr 258 max, ok := base.AddLength(maxMmapRand64) 259 if !ok { 260 panic(fmt.Sprintf("preferredPIELoadAddr %#x too large", base)) 261 } 262 263 if max > l.MaxAddr { 264 // preferredPIELoadAddr won't fit; fall back to the standard 265 // Linux behavior of 2/3 of TopDownBase. TSAN won't like this. 266 // 267 // Don't bother trying to shrink the randomization for now. 268 base = l.TopDownBase / 3 * 2 269 } 270 271 return base + mmapRand(maxMmapRand64) 272 } 273 274 // PtracePeekUser implements Context.PtracePeekUser. 275 func (c *Context64) PtracePeekUser(addr uintptr) (marshal.Marshallable, error) { 276 // TODO(gvisor.dev/issue/1239): Full ptrace supporting for Arm64. 277 return c.Native(0), nil 278 } 279 280 // PtracePokeUser implements Context.PtracePokeUser. 281 func (c *Context64) PtracePokeUser(addr, data uintptr) error { 282 // TODO(gvisor.dev/issue/1239): Full ptrace supporting for Arm64. 283 return nil 284 } 285 286 // FloatingPointData returns the state of the floating-point unit. 287 func (c *Context64) FloatingPointData() *fpu.State { 288 return &c.State.fpState 289 }