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 }