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  }