github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/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 // +build arm64 16 17 package arch 18 19 import ( 20 "fmt" 21 "math/rand" 22 23 "golang.org/x/sys/unix" 24 "github.com/SagerNet/gvisor/pkg/cpuid" 25 "github.com/SagerNet/gvisor/pkg/hostarch" 26 "github.com/SagerNet/gvisor/pkg/marshal" 27 "github.com/SagerNet/gvisor/pkg/marshal/primitive" 28 "github.com/SagerNet/gvisor/pkg/sentry/arch/fpu" 29 "github.com/SagerNet/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() Context { 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 // FeatureSet returns the FeatureSet in use. 185 func (c *context64) FeatureSet() *cpuid.FeatureSet { 186 return c.State.FeatureSet 187 } 188 189 // mmapRand returns a random adjustment for randomizing an mmap layout. 190 func mmapRand(max uint64) hostarch.Addr { 191 return hostarch.Addr(rand.Int63n(int64(max))).RoundDown() 192 } 193 194 // NewMmapLayout implements Context.NewMmapLayout consistently with Linux. 195 func (c *context64) NewMmapLayout(min, max hostarch.Addr, r *limits.LimitSet) (MmapLayout, error) { 196 min, ok := min.RoundUp() 197 if !ok { 198 return MmapLayout{}, unix.EINVAL 199 } 200 if max > maxAddr64 { 201 max = maxAddr64 202 } 203 max = max.RoundDown() 204 205 if min > max { 206 return MmapLayout{}, unix.EINVAL 207 } 208 209 stackSize := r.Get(limits.Stack) 210 211 // MAX_GAP in Linux. 212 maxGap := (max / 6) * 5 213 gap := hostarch.Addr(stackSize.Cur) 214 if gap < minGap64 { 215 gap = minGap64 216 } 217 if gap > maxGap { 218 gap = maxGap 219 } 220 defaultDir := MmapTopDown 221 if stackSize.Cur == limits.Infinity { 222 defaultDir = MmapBottomUp 223 } 224 225 topDownMin := max - gap - maxMmapRand64 226 maxRand := hostarch.Addr(maxMmapRand64) 227 if topDownMin < preferredTopDownBaseMin { 228 // Try to keep TopDownBase above preferredTopDownBaseMin by 229 // shrinking maxRand. 230 maxAdjust := maxRand - minMmapRand64 231 needAdjust := preferredTopDownBaseMin - topDownMin 232 if needAdjust <= maxAdjust { 233 maxRand -= needAdjust 234 } 235 } 236 237 rnd := mmapRand(uint64(maxRand)) 238 l := MmapLayout{ 239 MinAddr: min, 240 MaxAddr: max, 241 // TASK_UNMAPPED_BASE in Linux. 242 BottomUpBase: (max/3 + rnd).RoundDown(), 243 TopDownBase: (max - gap - rnd).RoundDown(), 244 DefaultDirection: defaultDir, 245 // We may have reduced the maximum randomization to keep 246 // TopDownBase above preferredTopDownBaseMin while maintaining 247 // our stack gap. Stack allocations must use that max 248 // randomization to avoiding eating into the gap. 249 MaxStackRand: uint64(maxRand), 250 } 251 252 // Final sanity check on the layout. 253 if !l.Valid() { 254 panic(fmt.Sprintf("Invalid MmapLayout: %+v", l)) 255 } 256 257 return l, nil 258 } 259 260 // PIELoadAddress implements Context.PIELoadAddress. 261 func (c *context64) PIELoadAddress(l MmapLayout) hostarch.Addr { 262 base := preferredPIELoadAddr 263 max, ok := base.AddLength(maxMmapRand64) 264 if !ok { 265 panic(fmt.Sprintf("preferredPIELoadAddr %#x too large", base)) 266 } 267 268 if max > l.MaxAddr { 269 // preferredPIELoadAddr won't fit; fall back to the standard 270 // Linux behavior of 2/3 of TopDownBase. TSAN won't like this. 271 // 272 // Don't bother trying to shrink the randomization for now. 273 base = l.TopDownBase / 3 * 2 274 } 275 276 return base + mmapRand(maxMmapRand64) 277 } 278 279 // PtracePeekUser implements Context.PtracePeekUser. 280 func (c *context64) PtracePeekUser(addr uintptr) (marshal.Marshallable, error) { 281 // TODO(github.com/SagerNet/issue/1239): Full ptrace supporting for Arm64. 282 return c.Native(0), nil 283 } 284 285 // PtracePokeUser implements Context.PtracePokeUser. 286 func (c *context64) PtracePokeUser(addr, data uintptr) error { 287 // TODO(github.com/SagerNet/issue/1239): Full ptrace supporting for Arm64. 288 return nil 289 } 290 291 func (c *context64) FloatingPointData() *fpu.State { 292 return &c.State.fpState 293 }