gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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 "gvisor.dev/gvisor/pkg/abi/linux" 21 "gvisor.dev/gvisor/pkg/context" 22 "gvisor.dev/gvisor/pkg/errors/linuxerr" 23 "gvisor.dev/gvisor/pkg/hostarch" 24 "gvisor.dev/gvisor/pkg/marshal" 25 "gvisor.dev/gvisor/pkg/sentry/mm" 26 "gvisor.dev/gvisor/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 switch t.Arch().Width() { 135 case 8: 136 if _, ok := addr.AddLength(uint64(src.NumRanges()) * iovecLength); !ok { 137 return linuxerr.EFAULT 138 } 139 140 b := t.CopyScratchBuffer(iovecLength) 141 for ; !src.IsEmpty(); src = src.Tail() { 142 ar := src.Head() 143 hostarch.ByteOrder.PutUint64(b[0:8], uint64(ar.Start)) 144 hostarch.ByteOrder.PutUint64(b[8:16], uint64(ar.Length())) 145 if _, err := t.CopyOutBytes(addr, b); err != nil { 146 return err 147 } 148 addr += iovecLength 149 } 150 151 default: 152 return linuxerr.ENOSYS 153 } 154 155 return nil 156 } 157 158 // CopyInIovecs copies in IoVecs for Task. 159 // 160 // Preconditions: Same as usermem.IO.CopyIn, plus: 161 // * The caller must be running on the task goroutine. 162 // * t's AddressSpace must be active. 163 func (t *Task) CopyInIovecs(addr hostarch.Addr, numIovecs int) (hostarch.AddrRangeSeq, error) { 164 // Special case to avoid allocating allocating a single hostaddr.AddrRange. 165 if numIovecs == 1 { 166 return copyInIovec(t, t, addr) 167 } 168 iovecs, err := copyInIovecs(t, t, addr, numIovecs) 169 if err != nil { 170 return hostarch.AddrRangeSeq{}, err 171 } 172 return hostarch.AddrRangeSeqFromSlice(iovecs), nil 173 } 174 175 // CopyInIovecsAsSlice copies in IoVecs and returns them in a slice. 176 // 177 // Preconditions: Same as usermem.IO.CopyIn, plus: 178 // - The caller must be running on the task goroutine or hold t.mu. 179 // - t's AddressSpace must be active. 180 func (t *Task) CopyInIovecsAsSlice(addr hostarch.Addr, numIovecs int) ([]hostarch.AddrRange, error) { 181 return copyInIovecs(t, t, addr, numIovecs) 182 } 183 184 func copyInIovec(ctx marshal.CopyContext, t *Task, addr hostarch.Addr) (hostarch.AddrRangeSeq, error) { 185 if err := checkArch(t); err != nil { 186 return hostarch.AddrRangeSeq{}, err 187 } 188 b := ctx.CopyScratchBuffer(iovecLength) 189 ar, err := makeIovec(ctx, t, addr, b) 190 if err != nil { 191 return hostarch.AddrRangeSeq{}, err 192 } 193 return hostarch.AddrRangeSeqOf(ar).TakeFirst(MAX_RW_COUNT), nil 194 } 195 196 // copyInIovecs copies an array of numIovecs struct iovecs from the memory 197 // mapped at addr, converts them to hostarch.AddrRanges, and returns them as a 198 // hostarch.AddrRangeSeq. 199 // 200 // copyInIovecs shares the following properties with Linux's 201 // lib/iov_iter.c:import_iovec() => fs/read_write.c:rw_copy_check_uvector(): 202 // 203 // - If the length of any AddrRange would exceed the range of an ssize_t, 204 // copyInIovecs returns EINVAL. 205 // 206 // - If the length of any AddrRange would cause its end to overflow, 207 // copyInIovecs returns EFAULT. 208 // 209 // - If any AddrRange would include addresses outside the application address 210 // range, copyInIovecs returns EFAULT. 211 // 212 // - The combined length of all AddrRanges is limited to MAX_RW_COUNT. If the 213 // combined length of all AddrRanges would otherwise exceed this amount, ranges 214 // beyond MAX_RW_COUNT are silently truncated. 215 func copyInIovecs(ctx marshal.CopyContext, t *Task, addr hostarch.Addr, numIovecs int) ([]hostarch.AddrRange, error) { 216 if err := checkArch(t); err != nil { 217 return nil, err 218 } 219 if numIovecs == 0 { 220 return nil, nil 221 } 222 223 var dst []hostarch.AddrRange 224 if numIovecs > 1 { 225 dst = make([]hostarch.AddrRange, 0, numIovecs) 226 } 227 228 if _, ok := addr.AddLength(uint64(numIovecs) * iovecLength); !ok { 229 return nil, linuxerr.EFAULT 230 } 231 232 b := ctx.CopyScratchBuffer(iovecLength) 233 for i := 0; i < numIovecs; i++ { 234 ar, err := makeIovec(ctx, t, addr, b) 235 if err != nil { 236 return []hostarch.AddrRange{}, err 237 } 238 dst = append(dst, ar) 239 240 addr += iovecLength 241 } 242 // Truncate to MAX_RW_COUNT. 243 var total uint64 244 for i := range dst { 245 dstlen := uint64(dst[i].Length()) 246 if rem := uint64(MAX_RW_COUNT) - total; rem < dstlen { 247 dst[i].End -= hostarch.Addr(dstlen - rem) 248 dstlen = rem 249 } 250 total += dstlen 251 } 252 253 return dst, nil 254 } 255 256 func checkArch(t *Task) error { 257 if t.Arch().Width() != 8 { 258 return linuxerr.ENOSYS 259 } 260 return nil 261 } 262 263 func makeIovec(ctx marshal.CopyContext, t *Task, addr hostarch.Addr, b []byte) (hostarch.AddrRange, error) { 264 if _, err := ctx.CopyInBytes(addr, b); err != nil { 265 return hostarch.AddrRange{}, err 266 } 267 268 base := hostarch.Addr(hostarch.ByteOrder.Uint64(b[0:8])) 269 length := hostarch.ByteOrder.Uint64(b[8:16]) 270 if length > math.MaxInt64 { 271 return hostarch.AddrRange{}, linuxerr.EINVAL 272 } 273 ar, ok := t.MemoryManager().CheckIORange(base, int64(length)) 274 if !ok { 275 return hostarch.AddrRange{}, linuxerr.EFAULT 276 } 277 return ar, nil 278 } 279 280 // SingleIOSequence returns a usermem.IOSequence representing [addr, 281 // addr+length) in t's address space. If this contains addresses outside the 282 // application address range, it returns EFAULT. If length exceeds 283 // MAX_RW_COUNT, the range is silently truncated. 284 // 285 // SingleIOSequence is analogous to Linux's 286 // lib/iov_iter.c:import_single_range(). (Note that the non-vectorized read and 287 // write syscalls in Linux do not use import_single_range(). However they check 288 // access_ok() in fs/read_write.c:vfs_read/vfs_write, and overflowing address 289 // ranges are truncated to MAX_RW_COUNT by fs/read_write.c:rw_verify_area().) 290 func (t *Task) SingleIOSequence(addr hostarch.Addr, length int, opts usermem.IOOpts) (usermem.IOSequence, error) { 291 if length > MAX_RW_COUNT { 292 length = MAX_RW_COUNT 293 } 294 ar, ok := t.MemoryManager().CheckIORange(addr, int64(length)) 295 if !ok { 296 return usermem.IOSequence{}, linuxerr.EFAULT 297 } 298 return usermem.IOSequence{ 299 IO: t.MemoryManager(), 300 Addrs: hostarch.AddrRangeSeqOf(ar), 301 Opts: opts, 302 }, nil 303 } 304 305 // IovecsIOSequence returns a usermem.IOSequence representing the array of 306 // iovcnt struct iovecs at addr in t's address space. opts applies to the 307 // returned IOSequence, not the reading of the struct iovec array. 308 // 309 // IovecsIOSequence is analogous to Linux's lib/iov_iter.c:import_iovec(). 310 // 311 // Preconditions: Same as Task.CopyInIovecs. 312 func (t *Task) IovecsIOSequence(addr hostarch.Addr, iovcnt int, opts usermem.IOOpts) (usermem.IOSequence, error) { 313 if iovcnt < 0 || iovcnt > linux.UIO_MAXIOV { 314 return usermem.IOSequence{}, linuxerr.EINVAL 315 } 316 ars, err := t.CopyInIovecs(addr, iovcnt) 317 if err != nil { 318 return usermem.IOSequence{}, err 319 } 320 return usermem.IOSequence{ 321 IO: t.MemoryManager(), 322 Addrs: ars, 323 Opts: opts, 324 }, nil 325 } 326 327 type taskCopyContext struct { 328 ctx context.Context 329 t *Task 330 opts usermem.IOOpts 331 } 332 333 // CopyContext returns a marshal.CopyContext that copies to/from t's address 334 // space using opts. 335 func (t *Task) CopyContext(ctx context.Context, opts usermem.IOOpts) *taskCopyContext { 336 return &taskCopyContext{ 337 ctx: ctx, 338 t: t, 339 opts: opts, 340 } 341 } 342 343 // CopyScratchBuffer implements marshal.CopyContext.CopyScratchBuffer. 344 func (cc *taskCopyContext) CopyScratchBuffer(size int) []byte { 345 if ctxTask, ok := cc.ctx.(*Task); ok { 346 return ctxTask.CopyScratchBuffer(size) 347 } 348 return make([]byte, size) 349 } 350 351 func (cc *taskCopyContext) getMemoryManager() (*mm.MemoryManager, error) { 352 tmm := cc.t.MemoryManager() 353 if tmm == nil { 354 return nil, linuxerr.ESRCH 355 } 356 if !tmm.IncUsers() { 357 return nil, linuxerr.EFAULT 358 } 359 return tmm, nil 360 } 361 362 // CopyInBytes implements marshal.CopyContext.CopyInBytes. 363 // 364 // Preconditions: Same as usermem.IO.CopyIn, plus: 365 // - The caller must be running on the task goroutine or hold the cc.t.mu 366 // - t's AddressSpace must be active. 367 func (cc *taskCopyContext) CopyInBytes(addr hostarch.Addr, dst []byte) (int, error) { 368 tmm, err := cc.getMemoryManager() 369 if err != nil { 370 return 0, err 371 } 372 defer tmm.DecUsers(cc.ctx) 373 return tmm.CopyIn(cc.ctx, addr, dst, cc.opts) 374 } 375 376 // CopyOutBytes implements marshal.CopyContext.CopyOutBytes. 377 // 378 // Preconditions: Same as usermem.IO.CopyOut, plus: 379 // - The caller must be running on the task goroutine or hold the cc.t.mu 380 // - t's AddressSpace must be active. 381 func (cc *taskCopyContext) CopyOutBytes(addr hostarch.Addr, src []byte) (int, error) { 382 tmm, err := cc.getMemoryManager() 383 if err != nil { 384 return 0, err 385 } 386 defer tmm.DecUsers(cc.ctx) 387 return tmm.CopyOut(cc.ctx, addr, src, cc.opts) 388 } 389 390 type ownTaskCopyContext struct { 391 t *Task 392 opts usermem.IOOpts 393 } 394 395 // OwnCopyContext returns a marshal.CopyContext that copies to/from t's address 396 // space using opts. The returned CopyContext may only be used by t's task 397 // goroutine. 398 // 399 // Since t already implements marshal.CopyContext, this is only needed to 400 // override the usermem.IOOpts used for the copy. 401 func (t *Task) OwnCopyContext(opts usermem.IOOpts) *ownTaskCopyContext { 402 return &ownTaskCopyContext{ 403 t: t, 404 opts: opts, 405 } 406 } 407 408 // CopyScratchBuffer implements marshal.CopyContext.CopyScratchBuffer. 409 func (cc *ownTaskCopyContext) CopyScratchBuffer(size int) []byte { 410 return cc.t.CopyScratchBuffer(size) 411 } 412 413 // CopyInBytes implements marshal.CopyContext.CopyInBytes. 414 func (cc *ownTaskCopyContext) CopyInBytes(addr hostarch.Addr, dst []byte) (int, error) { 415 return cc.t.MemoryManager().CopyIn(cc.t, addr, dst, cc.opts) 416 } 417 418 // CopyOutBytes implements marshal.CopyContext.CopyOutBytes. 419 func (cc *ownTaskCopyContext) CopyOutBytes(addr hostarch.Addr, src []byte) (int, error) { 420 return cc.t.MemoryManager().CopyOut(cc.t, addr, src, cc.opts) 421 }