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  }