github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/kernel/task_usermem.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 kernel 16 17 import ( 18 "math" 19 20 "github.com/nicocha30/gvisor-ligolo/pkg/abi/linux" 21 "github.com/nicocha30/gvisor-ligolo/pkg/context" 22 "github.com/nicocha30/gvisor-ligolo/pkg/errors/linuxerr" 23 "github.com/nicocha30/gvisor-ligolo/pkg/hostarch" 24 "github.com/nicocha30/gvisor-ligolo/pkg/marshal" 25 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/mm" 26 "github.com/nicocha30/gvisor-ligolo/pkg/usermem" 27 ) 28 29 const iovecLength = 16 30 31 // MAX_RW_COUNT is the maximum size in bytes of a single read or write. 32 // Reads and writes that exceed this size may be silently truncated. 33 // (Linux: include/linux/fs.h:MAX_RW_COUNT) 34 var MAX_RW_COUNT = int(hostarch.Addr(math.MaxInt32).RoundDown()) 35 36 // Activate ensures that the task has an active address space. 37 func (t *Task) Activate() { 38 if mm := t.MemoryManager(); mm != nil { 39 if err := mm.Activate(t); err != nil { 40 panic("unable to activate mm: " + err.Error()) 41 } 42 } 43 } 44 45 // Deactivate relinquishes the task's active address space. 46 func (t *Task) Deactivate() { 47 if mm := t.MemoryManager(); mm != nil { 48 mm.Deactivate() 49 } 50 } 51 52 // CopyInBytes is a fast version of CopyIn if the caller can serialize the 53 // data without reflection and pass in a byte slice. 54 // 55 // This Task's AddressSpace must be active. 56 func (t *Task) CopyInBytes(addr hostarch.Addr, dst []byte) (int, error) { 57 return t.MemoryManager().CopyIn(t, addr, dst, usermem.IOOpts{ 58 AddressSpaceActive: true, 59 }) 60 } 61 62 // CopyOutBytes is a fast version of CopyOut if the caller can serialize the 63 // data without reflection and pass in a byte slice. 64 // 65 // This Task's AddressSpace must be active. 66 func (t *Task) CopyOutBytes(addr hostarch.Addr, src []byte) (int, error) { 67 return t.MemoryManager().CopyOut(t, addr, src, usermem.IOOpts{ 68 AddressSpaceActive: true, 69 }) 70 } 71 72 // CopyInString copies a NUL-terminated string of length at most maxlen in from 73 // the task's memory. The copy will fail with syscall.EFAULT if it traverses 74 // user memory that is unmapped or not readable by the user. 75 // 76 // This Task's AddressSpace must be active. 77 func (t *Task) CopyInString(addr hostarch.Addr, maxlen int) (string, error) { 78 return usermem.CopyStringIn(t, t.MemoryManager(), addr, maxlen, usermem.IOOpts{ 79 AddressSpaceActive: true, 80 }) 81 } 82 83 // CopyInVector copies a NULL-terminated vector of strings from the task's 84 // memory. The copy will fail with syscall.EFAULT if it traverses 85 // user memory that is unmapped or not readable by the user. 86 // 87 // maxElemSize is the maximum size of each individual element. 88 // 89 // maxTotalSize is the maximum total length of all elements plus the total 90 // number of elements. For example, the following strings correspond to 91 // the following set of sizes: 92 // 93 // { "a", "b", "c" } => 6 (3 for lengths, 3 for elements) 94 // { "abc" } => 4 (3 for length, 1 for elements) 95 // 96 // This Task's AddressSpace must be active. 97 func (t *Task) CopyInVector(addr hostarch.Addr, maxElemSize, maxTotalSize int) ([]string, error) { 98 var v []string 99 for { 100 argAddr := t.Arch().Native(0) 101 if _, err := argAddr.CopyIn(t, addr); err != nil { 102 return v, err 103 } 104 if t.Arch().Value(argAddr) == 0 { 105 break 106 } 107 // Each string has a zero terminating byte counted, so copying out a string 108 // requires at least one byte of space. Also, see the calculation below. 109 if maxTotalSize <= 0 { 110 return nil, linuxerr.ENOMEM 111 } 112 thisMax := maxElemSize 113 if maxTotalSize < thisMax { 114 thisMax = maxTotalSize 115 } 116 arg, err := t.CopyInString(hostarch.Addr(t.Arch().Value(argAddr)), thisMax) 117 if err != nil { 118 return v, err 119 } 120 v = append(v, arg) 121 addr += hostarch.Addr(t.Arch().Width()) 122 maxTotalSize -= len(arg) + 1 123 } 124 return v, nil 125 } 126 127 // CopyOutIovecs converts src to an array of struct iovecs and copies it to the 128 // memory mapped at addr for Task. 129 // 130 // Preconditions: Same as usermem.IO.CopyOut, plus: 131 // - The caller must be running on the task goroutine. 132 // - t's AddressSpace must be active. 133 func (t *Task) CopyOutIovecs(addr hostarch.Addr, src hostarch.AddrRangeSeq) error { 134 return copyOutIovecs(t, t, addr, src) 135 } 136 137 // copyOutIovecs converts src to an array of struct iovecs and copies it to the 138 // memory mapped at addr. 139 func copyOutIovecs(ctx marshal.CopyContext, t *Task, addr hostarch.Addr, src hostarch.AddrRangeSeq) error { 140 switch t.Arch().Width() { 141 case 8: 142 if _, ok := addr.AddLength(uint64(src.NumRanges()) * iovecLength); !ok { 143 return linuxerr.EFAULT 144 } 145 146 b := ctx.CopyScratchBuffer(iovecLength) 147 for ; !src.IsEmpty(); src = src.Tail() { 148 ar := src.Head() 149 hostarch.ByteOrder.PutUint64(b[0:8], uint64(ar.Start)) 150 hostarch.ByteOrder.PutUint64(b[8:16], uint64(ar.Length())) 151 if _, err := ctx.CopyOutBytes(addr, b); err != nil { 152 return err 153 } 154 addr += iovecLength 155 } 156 157 default: 158 return linuxerr.ENOSYS 159 } 160 161 return nil 162 } 163 164 // CopyInIovecs copies in IoVecs for Task. 165 // 166 // Preconditions: Same as usermem.IO.CopyIn, plus: 167 // * The caller must be running on the task goroutine. 168 // * t's AddressSpace must be active. 169 func (t *Task) CopyInIovecs(addr hostarch.Addr, numIovecs int) (hostarch.AddrRangeSeq, error) { 170 // Special case to avoid allocating allocating a single hostaddr.AddrRange. 171 if numIovecs == 1 { 172 return copyInIovec(t, t, addr) 173 } 174 iovecs, err := copyInIovecs(t, t, addr, numIovecs) 175 if err != nil { 176 return hostarch.AddrRangeSeq{}, err 177 } 178 return hostarch.AddrRangeSeqFromSlice(iovecs), nil 179 } 180 181 func copyInIovec(ctx marshal.CopyContext, t *Task, addr hostarch.Addr) (hostarch.AddrRangeSeq, error) { 182 if err := checkArch(t); err != nil { 183 return hostarch.AddrRangeSeq{}, err 184 } 185 b := ctx.CopyScratchBuffer(iovecLength) 186 ar, err := makeIovec(ctx, t, addr, b) 187 if err != nil { 188 return hostarch.AddrRangeSeq{}, err 189 } 190 return hostarch.AddrRangeSeqOf(ar).TakeFirst(MAX_RW_COUNT), nil 191 } 192 193 // copyInIovecs copies an array of numIovecs struct iovecs from the memory 194 // mapped at addr, converts them to hostarch.AddrRanges, and returns them as a 195 // hostarch.AddrRangeSeq. 196 // 197 // copyInIovecs shares the following properties with Linux's 198 // lib/iov_iter.c:import_iovec() => fs/read_write.c:rw_copy_check_uvector(): 199 // 200 // - If the length of any AddrRange would exceed the range of an ssize_t, 201 // copyInIovecs returns EINVAL. 202 // 203 // - If the length of any AddrRange would cause its end to overflow, 204 // copyInIovecs returns EFAULT. 205 // 206 // - If any AddrRange would include addresses outside the application address 207 // range, copyInIovecs returns EFAULT. 208 // 209 // - The combined length of all AddrRanges is limited to MAX_RW_COUNT. If the 210 // combined length of all AddrRanges would otherwise exceed this amount, ranges 211 // beyond MAX_RW_COUNT are silently truncated. 212 func copyInIovecs(ctx marshal.CopyContext, t *Task, addr hostarch.Addr, numIovecs int) ([]hostarch.AddrRange, error) { 213 if err := checkArch(t); err != nil { 214 return nil, err 215 } 216 if numIovecs == 0 { 217 return nil, nil 218 } 219 220 var dst []hostarch.AddrRange 221 if numIovecs > 1 { 222 dst = make([]hostarch.AddrRange, 0, numIovecs) 223 } 224 225 if _, ok := addr.AddLength(uint64(numIovecs) * iovecLength); !ok { 226 return nil, linuxerr.EFAULT 227 } 228 229 b := ctx.CopyScratchBuffer(iovecLength) 230 for i := 0; i < numIovecs; i++ { 231 ar, err := makeIovec(ctx, t, addr, b) 232 if err != nil { 233 return []hostarch.AddrRange{}, err 234 } 235 dst = append(dst, ar) 236 237 addr += iovecLength 238 } 239 // Truncate to MAX_RW_COUNT. 240 var total uint64 241 for i := range dst { 242 dstlen := uint64(dst[i].Length()) 243 if rem := uint64(MAX_RW_COUNT) - total; rem < dstlen { 244 dst[i].End -= hostarch.Addr(dstlen - rem) 245 dstlen = rem 246 } 247 total += dstlen 248 } 249 250 return dst, nil 251 } 252 253 func checkArch(t *Task) error { 254 if t.Arch().Width() != 8 { 255 return linuxerr.ENOSYS 256 } 257 return nil 258 } 259 260 func makeIovec(ctx marshal.CopyContext, t *Task, addr hostarch.Addr, b []byte) (hostarch.AddrRange, error) { 261 if _, err := ctx.CopyInBytes(addr, b); err != nil { 262 return hostarch.AddrRange{}, err 263 } 264 265 base := hostarch.Addr(hostarch.ByteOrder.Uint64(b[0:8])) 266 length := hostarch.ByteOrder.Uint64(b[8:16]) 267 if length > math.MaxInt64 { 268 return hostarch.AddrRange{}, linuxerr.EINVAL 269 } 270 ar, ok := t.MemoryManager().CheckIORange(base, int64(length)) 271 if !ok { 272 return hostarch.AddrRange{}, linuxerr.EFAULT 273 } 274 return ar, nil 275 } 276 277 // SingleIOSequence returns a usermem.IOSequence representing [addr, 278 // addr+length) in t's address space. If this contains addresses outside the 279 // application address range, it returns EFAULT. If length exceeds 280 // MAX_RW_COUNT, the range is silently truncated. 281 // 282 // SingleIOSequence is analogous to Linux's 283 // lib/iov_iter.c:import_single_range(). (Note that the non-vectorized read and 284 // write syscalls in Linux do not use import_single_range(). However they check 285 // access_ok() in fs/read_write.c:vfs_read/vfs_write, and overflowing address 286 // ranges are truncated to MAX_RW_COUNT by fs/read_write.c:rw_verify_area().) 287 func (t *Task) SingleIOSequence(addr hostarch.Addr, length int, opts usermem.IOOpts) (usermem.IOSequence, error) { 288 if length > MAX_RW_COUNT { 289 length = MAX_RW_COUNT 290 } 291 ar, ok := t.MemoryManager().CheckIORange(addr, int64(length)) 292 if !ok { 293 return usermem.IOSequence{}, linuxerr.EFAULT 294 } 295 return usermem.IOSequence{ 296 IO: t.MemoryManager(), 297 Addrs: hostarch.AddrRangeSeqOf(ar), 298 Opts: opts, 299 }, nil 300 } 301 302 // IovecsIOSequence returns a usermem.IOSequence representing the array of 303 // iovcnt struct iovecs at addr in t's address space. opts applies to the 304 // returned IOSequence, not the reading of the struct iovec array. 305 // 306 // IovecsIOSequence is analogous to Linux's lib/iov_iter.c:import_iovec(). 307 // 308 // Preconditions: Same as Task.CopyInIovecs. 309 func (t *Task) IovecsIOSequence(addr hostarch.Addr, iovcnt int, opts usermem.IOOpts) (usermem.IOSequence, error) { 310 if iovcnt < 0 || iovcnt > linux.UIO_MAXIOV { 311 return usermem.IOSequence{}, linuxerr.EINVAL 312 } 313 ars, err := t.CopyInIovecs(addr, iovcnt) 314 if err != nil { 315 return usermem.IOSequence{}, err 316 } 317 return usermem.IOSequence{ 318 IO: t.MemoryManager(), 319 Addrs: ars, 320 Opts: opts, 321 }, nil 322 } 323 324 type taskCopyContext struct { 325 ctx context.Context 326 t *Task 327 opts usermem.IOOpts 328 allocateNewBuffers bool 329 } 330 331 // CopyContext returns a marshal.CopyContext that copies to/from t's address 332 // space using opts. 333 func (t *Task) CopyContext(ctx context.Context, opts usermem.IOOpts) *taskCopyContext { 334 return &taskCopyContext{ 335 ctx: ctx, 336 t: t, 337 opts: opts, 338 } 339 } 340 341 // CopyScratchBuffer implements marshal.CopyContext.CopyScratchBuffer. 342 func (cc *taskCopyContext) CopyScratchBuffer(size int) []byte { 343 if ctxTask, ok := cc.ctx.(*Task); ok && !cc.allocateNewBuffers { 344 return ctxTask.CopyScratchBuffer(size) 345 } 346 return make([]byte, size) 347 } 348 349 func (cc *taskCopyContext) getMemoryManager() (*mm.MemoryManager, error) { 350 tmm := cc.t.MemoryManager() 351 if tmm == nil { 352 return nil, linuxerr.ESRCH 353 } 354 if !tmm.IncUsers() { 355 return nil, linuxerr.EFAULT 356 } 357 return tmm, nil 358 } 359 360 // WithTaskMutexLocked runs the given function with the task's mutex locked. 361 func (cc *taskCopyContext) WithTaskMutexLocked(fn func() error) error { 362 cc.t.mu.Lock() 363 defer cc.t.mu.Unlock() 364 return fn() 365 } 366 367 // CopyInBytes implements marshal.CopyContext.CopyInBytes. 368 // 369 // Preconditions: Same as usermem.IO.CopyIn, plus: 370 // - The caller must be running on the task goroutine or hold the cc.t.mu 371 // - t's AddressSpace must be active. 372 func (cc *taskCopyContext) CopyInBytes(addr hostarch.Addr, dst []byte) (int, error) { 373 tmm, err := cc.getMemoryManager() 374 if err != nil { 375 return 0, err 376 } 377 defer tmm.DecUsers(cc.ctx) 378 return tmm.CopyIn(cc.ctx, addr, dst, cc.opts) 379 } 380 381 // CopyOutBytes implements marshal.CopyContext.CopyOutBytes. 382 // 383 // Preconditions: Same as usermem.IO.CopyOut, plus: 384 // - The caller must be running on the task goroutine or hold the cc.t.mu 385 // - t's AddressSpace must be active. 386 func (cc *taskCopyContext) CopyOutBytes(addr hostarch.Addr, src []byte) (int, error) { 387 tmm, err := cc.getMemoryManager() 388 if err != nil { 389 return 0, err 390 } 391 defer tmm.DecUsers(cc.ctx) 392 return tmm.CopyOut(cc.ctx, addr, src, cc.opts) 393 } 394 395 // CopyOutIovecs converts src to an array of struct iovecs and copies it to the 396 // memory mapped at addr for Task. 397 // 398 // Preconditions: Same as usermem.IO.CopyOut, plus: 399 // - The caller must be running on the task goroutine or hold the cc.t.mu 400 // - t's AddressSpace must be active. 401 func (cc *taskCopyContext) CopyOutIovecs(addr hostarch.Addr, src hostarch.AddrRangeSeq) error { 402 return copyOutIovecs(cc, cc.t, addr, src) 403 } 404 405 // CopyInIovecs copies in IoVecs for taskCopyContext. 406 // 407 // Preconditions: Same as usermem.IO.CopyIn, plus: 408 // - The caller must be running on the task goroutine or hold the cc.t.mu 409 // - t's AddressSpace must be active. 410 func (cc *taskCopyContext) CopyInIovecs(addr hostarch.Addr, numIovecs int) ([]hostarch.AddrRange, error) { 411 return copyInIovecs(cc, cc.t, addr, numIovecs) 412 } 413 414 type ownTaskCopyContext struct { 415 t *Task 416 opts usermem.IOOpts 417 } 418 419 // OwnCopyContext returns a marshal.CopyContext that copies to/from t's address 420 // space using opts. The returned CopyContext may only be used by t's task 421 // goroutine. 422 // 423 // Since t already implements marshal.CopyContext, this is only needed to 424 // override the usermem.IOOpts used for the copy. 425 func (t *Task) OwnCopyContext(opts usermem.IOOpts) *ownTaskCopyContext { 426 return &ownTaskCopyContext{ 427 t: t, 428 opts: opts, 429 } 430 } 431 432 // CopyScratchBuffer implements marshal.CopyContext.CopyScratchBuffer. 433 func (cc *ownTaskCopyContext) CopyScratchBuffer(size int) []byte { 434 return cc.t.CopyScratchBuffer(size) 435 } 436 437 // CopyInBytes implements marshal.CopyContext.CopyInBytes. 438 func (cc *ownTaskCopyContext) CopyInBytes(addr hostarch.Addr, dst []byte) (int, error) { 439 return cc.t.MemoryManager().CopyIn(cc.t, addr, dst, cc.opts) 440 } 441 442 // CopyOutBytes implements marshal.CopyContext.CopyOutBytes. 443 func (cc *ownTaskCopyContext) CopyOutBytes(addr hostarch.Addr, src []byte) (int, error) { 444 return cc.t.MemoryManager().CopyOut(cc.t, addr, src, cc.opts) 445 }