github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/arch/stack.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 arch
    16  
    17  import (
    18  	"github.com/nicocha30/gvisor-ligolo/pkg/context"
    19  	"github.com/nicocha30/gvisor-ligolo/pkg/hostarch"
    20  	"github.com/nicocha30/gvisor-ligolo/pkg/marshal/primitive"
    21  
    22  	"github.com/nicocha30/gvisor-ligolo/pkg/usermem"
    23  )
    24  
    25  // Stack is a simple wrapper around a hostarch.IO and an address. Stack
    26  // implements marshal.CopyContext, and marshallable values can be pushed or
    27  // popped from the stack through the marshal.Marshallable interface.
    28  //
    29  // Stack is not thread-safe.
    30  type Stack struct {
    31  	// Our arch info.
    32  	// We use this for automatic Native conversion of hostarch.Addrs during
    33  	// Push() and Pop().
    34  	Arch *Context64
    35  
    36  	// The interface used to actually copy user memory.
    37  	IO usermem.IO
    38  
    39  	// Our current stack bottom.
    40  	Bottom hostarch.Addr
    41  
    42  	// Scratch buffer used for marshalling to avoid having to repeatedly
    43  	// allocate scratch memory.
    44  	scratchBuf []byte
    45  }
    46  
    47  // scratchBufLen is the default length of Stack.scratchBuf. The
    48  // largest structs the stack regularly serializes are linux.SignalInfo
    49  // and arch.UContext64. We'll set the default size as the larger of
    50  // the two, arch.UContext64.
    51  var scratchBufLen = (*UContext64)(nil).SizeBytes()
    52  
    53  // CopyScratchBuffer implements marshal.CopyContext.CopyScratchBuffer.
    54  func (s *Stack) CopyScratchBuffer(size int) []byte {
    55  	if len(s.scratchBuf) < size {
    56  		s.scratchBuf = make([]byte, size)
    57  	}
    58  	return s.scratchBuf[:size]
    59  }
    60  
    61  // StackBottomMagic is the special address callers must past to all stack
    62  // marshalling operations to cause the src/dst address to be computed based on
    63  // the current end of the stack.
    64  const StackBottomMagic = ^hostarch.Addr(0) // hostarch.Addr(-1)
    65  
    66  // CopyOutBytes implements marshal.CopyContext.CopyOutBytes. CopyOutBytes
    67  // computes an appropriate address based on the current end of the
    68  // stack. Callers use the sentinel address StackBottomMagic to marshal methods
    69  // to indicate this.
    70  func (s *Stack) CopyOutBytes(sentinel hostarch.Addr, b []byte) (int, error) {
    71  	if sentinel != StackBottomMagic {
    72  		panic("Attempted to copy out to stack with absolute address")
    73  	}
    74  	c := len(b)
    75  	n, err := s.IO.CopyOut(context.Background(), s.Bottom-hostarch.Addr(c), b, usermem.IOOpts{})
    76  	if err == nil && n == c {
    77  		s.Bottom -= hostarch.Addr(n)
    78  	}
    79  	return n, err
    80  }
    81  
    82  // CopyInBytes implements marshal.CopyContext.CopyInBytes. CopyInBytes computes
    83  // an appropriate address based on the current end of the stack. Callers must
    84  // use the sentinel address StackBottomMagic to marshal methods to indicate
    85  // this.
    86  func (s *Stack) CopyInBytes(sentinel hostarch.Addr, b []byte) (int, error) {
    87  	if sentinel != StackBottomMagic {
    88  		panic("Attempted to copy in from stack with absolute address")
    89  	}
    90  	n, err := s.IO.CopyIn(context.Background(), s.Bottom, b, usermem.IOOpts{})
    91  	if err == nil {
    92  		s.Bottom += hostarch.Addr(n)
    93  	}
    94  	return n, err
    95  }
    96  
    97  // Align aligns the stack to the given offset.
    98  func (s *Stack) Align(offset int) {
    99  	if s.Bottom%hostarch.Addr(offset) != 0 {
   100  		s.Bottom -= (s.Bottom % hostarch.Addr(offset))
   101  	}
   102  }
   103  
   104  // PushNullTerminatedByteSlice writes bs to the stack, followed by an extra null
   105  // byte at the end. On error, the contents of the stack and the bottom cursor
   106  // are undefined.
   107  func (s *Stack) PushNullTerminatedByteSlice(bs []byte) (int, error) {
   108  	// Note: Stack grows up, so write the terminal null byte first.
   109  	nNull, err := primitive.CopyUint8Out(s, StackBottomMagic, 0)
   110  	if err != nil {
   111  		return 0, err
   112  	}
   113  	n, err := primitive.CopyByteSliceOut(s, StackBottomMagic, bs)
   114  	if err != nil {
   115  		return 0, err
   116  	}
   117  	return n + nNull, nil
   118  }
   119  
   120  // StackLayout describes the location of the arguments and environment on the
   121  // stack.
   122  type StackLayout struct {
   123  	// ArgvStart is the beginning of the argument vector.
   124  	ArgvStart hostarch.Addr
   125  
   126  	// ArgvEnd is the end of the argument vector.
   127  	ArgvEnd hostarch.Addr
   128  
   129  	// EnvvStart is the beginning of the environment vector.
   130  	EnvvStart hostarch.Addr
   131  
   132  	// EnvvEnd is the end of the environment vector.
   133  	EnvvEnd hostarch.Addr
   134  }
   135  
   136  // Load pushes the given args, env and aux vector to the stack using the
   137  // well-known format for a new executable. It returns the start and end
   138  // of the argument and environment vectors.
   139  func (s *Stack) Load(args []string, env []string, aux Auxv) (StackLayout, error) {
   140  	l := StackLayout{}
   141  
   142  	// Make sure we start with a 16-byte alignment.
   143  	s.Align(16)
   144  
   145  	// Push the environment vector so the end of the argument vector is adjacent to
   146  	// the beginning of the environment vector.
   147  	// While the System V abi for x86_64 does not specify an ordering to the
   148  	// Information Block (the block holding the arg, env, and aux vectors),
   149  	// support features like setproctitle(3) naturally expect these segments
   150  	// to be in this order. See: https://www.uclibc.org/docs/psABI-x86_64.pdf
   151  	// page 29.
   152  	l.EnvvEnd = s.Bottom
   153  	envAddrs := make([]hostarch.Addr, len(env))
   154  	for i := len(env) - 1; i >= 0; i-- {
   155  		if _, err := s.PushNullTerminatedByteSlice([]byte(env[i])); err != nil {
   156  			return StackLayout{}, err
   157  		}
   158  		envAddrs[i] = s.Bottom
   159  	}
   160  	l.EnvvStart = s.Bottom
   161  
   162  	// Push our strings.
   163  	l.ArgvEnd = s.Bottom
   164  	argAddrs := make([]hostarch.Addr, len(args))
   165  	for i := len(args) - 1; i >= 0; i-- {
   166  		if _, err := s.PushNullTerminatedByteSlice([]byte(args[i])); err != nil {
   167  			return StackLayout{}, err
   168  		}
   169  		argAddrs[i] = s.Bottom
   170  	}
   171  	l.ArgvStart = s.Bottom
   172  
   173  	// We need to align the arguments appropriately.
   174  	//
   175  	// We must finish on a 16-byte alignment, but we'll play it
   176  	// conservatively and finish at 32-bytes. It would be nice to be able
   177  	// to call Align here, but unfortunately we need to align the stack
   178  	// with all the variable sized arrays pushed. So we just need to do
   179  	// some calculations.
   180  	argvSize := s.Arch.Width() * uint(len(args)+1)
   181  	envvSize := s.Arch.Width() * uint(len(env)+1)
   182  	auxvSize := s.Arch.Width() * 2 * uint(len(aux)+1)
   183  	total := hostarch.Addr(argvSize) + hostarch.Addr(envvSize) + hostarch.Addr(auxvSize) + hostarch.Addr(s.Arch.Width())
   184  	expectedBottom := s.Bottom - total
   185  	if expectedBottom%32 != 0 {
   186  		s.Bottom -= expectedBottom % 32
   187  	}
   188  
   189  	// Push our auxvec.
   190  	// NOTE: We need an extra zero here per spec.
   191  	// The Push function will automatically terminate
   192  	// strings and arrays with a single null value.
   193  	auxv := make([]hostarch.Addr, 0, len(aux)*2+1)
   194  	for _, a := range aux {
   195  		auxv = append(auxv, hostarch.Addr(a.Key), a.Value)
   196  	}
   197  	auxv = append(auxv, hostarch.Addr(0))
   198  	_, err := s.pushAddrSliceAndTerminator(auxv)
   199  	if err != nil {
   200  		return StackLayout{}, err
   201  	}
   202  
   203  	// Push environment.
   204  	_, err = s.pushAddrSliceAndTerminator(envAddrs)
   205  	if err != nil {
   206  		return StackLayout{}, err
   207  	}
   208  
   209  	// Push args.
   210  	_, err = s.pushAddrSliceAndTerminator(argAddrs)
   211  	if err != nil {
   212  		return StackLayout{}, err
   213  	}
   214  
   215  	// Push arg count.
   216  	lenP := s.Arch.Native(uintptr(len(args)))
   217  	if _, err = lenP.CopyOut(s, StackBottomMagic); err != nil {
   218  		return StackLayout{}, err
   219  	}
   220  
   221  	return l, nil
   222  }