github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/usermem/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 usermem governs access to user memory. 16 package usermem 17 18 import ( 19 "bytes" 20 "errors" 21 "io" 22 "strconv" 23 24 "github.com/nicocha30/gvisor-ligolo/pkg/context" 25 "github.com/nicocha30/gvisor-ligolo/pkg/errors/linuxerr" 26 "github.com/nicocha30/gvisor-ligolo/pkg/gohacks" 27 "github.com/nicocha30/gvisor-ligolo/pkg/hostarch" 28 "github.com/nicocha30/gvisor-ligolo/pkg/safemem" 29 ) 30 31 // IO provides access to the contents of a virtual memory space. 32 type IO interface { 33 // CopyOut copies len(src) bytes from src to the memory mapped at addr. It 34 // returns the number of bytes copied. If the number of bytes copied is < 35 // len(src), it returns a non-nil error explaining why. 36 // 37 // Preconditions: The caller must not hold mm.MemoryManager.mappingMu or 38 // any following locks in the lock order. 39 // 40 // Postconditions: CopyOut does not retain src. 41 CopyOut(ctx context.Context, addr hostarch.Addr, src []byte, opts IOOpts) (int, error) 42 43 // CopyIn copies len(dst) bytes from the memory mapped at addr to dst. 44 // It returns the number of bytes copied. If the number of bytes copied is 45 // < len(dst), it returns a non-nil error explaining why. 46 // 47 // Preconditions: The caller must not hold mm.MemoryManager.mappingMu or 48 // any following locks in the lock order. 49 // 50 // Postconditions: CopyIn does not retain dst. 51 CopyIn(ctx context.Context, addr hostarch.Addr, dst []byte, opts IOOpts) (int, error) 52 53 // ZeroOut sets toZero bytes to 0, starting at addr. It returns the number 54 // of bytes zeroed. If the number of bytes zeroed is < toZero, it returns a 55 // non-nil error explaining why. 56 // 57 // Preconditions: 58 // * The caller must not hold mm.MemoryManager.mappingMu or any 59 // following locks in the lock order. 60 // * toZero >= 0. 61 ZeroOut(ctx context.Context, addr hostarch.Addr, toZero int64, opts IOOpts) (int64, error) 62 63 // CopyOutFrom copies ars.NumBytes() bytes from src to the memory mapped at 64 // ars. It returns the number of bytes copied, which may be less than the 65 // number of bytes read from src if copying fails. CopyOutFrom may return a 66 // partial copy without an error iff src.ReadToBlocks returns a partial 67 // read without an error. 68 // 69 // CopyOutFrom calls src.ReadToBlocks at most once. 70 // 71 // Preconditions: 72 // * The caller must not hold mm.MemoryManager.mappingMu or any 73 // following locks in the lock order. 74 // * src.ReadToBlocks must not block on mm.MemoryManager.activeMu or 75 // any preceding locks in the lock order. 76 CopyOutFrom(ctx context.Context, ars hostarch.AddrRangeSeq, src safemem.Reader, opts IOOpts) (int64, error) 77 78 // CopyInTo copies ars.NumBytes() bytes from the memory mapped at ars to 79 // dst. It returns the number of bytes copied. CopyInTo may return a 80 // partial copy without an error iff dst.WriteFromBlocks returns a partial 81 // write without an error. 82 // 83 // CopyInTo calls dst.WriteFromBlocks at most once. 84 // 85 // Preconditions: 86 // * The caller must not hold mm.MemoryManager.mappingMu or any 87 // following locks in the lock order. 88 // * dst.WriteFromBlocks must not block on mm.MemoryManager.activeMu or 89 // any preceding locks in the lock order. 90 CopyInTo(ctx context.Context, ars hostarch.AddrRangeSeq, dst safemem.Writer, opts IOOpts) (int64, error) 91 92 // TODO(jamieliu): The requirement that CopyOutFrom/CopyInTo call src/dst 93 // at most once, which is unnecessary in most cases, forces implementations 94 // to gather safemem.Blocks into a single slice to pass to src/dst. Add 95 // CopyOutFromIter/CopyInToIter, which relaxes this restriction, to avoid 96 // this allocation. 97 98 // SwapUint32 atomically sets the uint32 value at addr to new and 99 // returns the previous value. 100 // 101 // Preconditions: 102 // * The caller must not hold mm.MemoryManager.mappingMu or any 103 // following locks in the lock order. 104 // * addr must be aligned to a 4-byte boundary. 105 SwapUint32(ctx context.Context, addr hostarch.Addr, new uint32, opts IOOpts) (uint32, error) 106 107 // CompareAndSwapUint32 atomically compares the uint32 value at addr to 108 // old; if they are equal, the value in memory is replaced by new. In 109 // either case, the previous value stored in memory is returned. 110 // 111 // Preconditions: 112 // * The caller must not hold mm.MemoryManager.mappingMu or any 113 // following locks in the lock order. 114 // * addr must be aligned to a 4-byte boundary. 115 CompareAndSwapUint32(ctx context.Context, addr hostarch.Addr, old, new uint32, opts IOOpts) (uint32, error) 116 117 // LoadUint32 atomically loads the uint32 value at addr and returns it. 118 // 119 // Preconditions: 120 // * The caller must not hold mm.MemoryManager.mappingMu or any 121 // following locks in the lock order. 122 // * addr must be aligned to a 4-byte boundary. 123 LoadUint32(ctx context.Context, addr hostarch.Addr, opts IOOpts) (uint32, error) 124 } 125 126 // IOOpts contains options applicable to all IO methods. 127 type IOOpts struct { 128 // If IgnorePermissions is true, application-defined memory protections set 129 // by mmap(2) or mprotect(2) will be ignored. (Memory protections required 130 // by the target of the mapping are never ignored.) 131 IgnorePermissions bool 132 133 // If AddressSpaceActive is true, the IO implementation may assume that it 134 // has an active AddressSpace and can therefore use AddressSpace copying 135 // without performing activation. See mm/io.go for details. 136 AddressSpaceActive bool 137 } 138 139 // IOReadWriter is an io.ReadWriter that reads from / writes to addresses 140 // starting at addr in IO. The preconditions that apply to IO.CopyIn and 141 // IO.CopyOut also apply to IOReadWriter.Read and IOReadWriter.Write 142 // respectively. 143 type IOReadWriter struct { 144 Ctx context.Context 145 IO IO 146 Addr hostarch.Addr 147 Opts IOOpts 148 } 149 150 // Read implements io.Reader.Read. 151 // 152 // Note that an address space does not have an "end of file", so Read can only 153 // return io.EOF if IO.CopyIn returns io.EOF. Attempts to read unmapped or 154 // unreadable memory, or beyond the end of the address space, should return 155 // EFAULT. 156 func (rw *IOReadWriter) Read(dst []byte) (int, error) { 157 n, err := rw.IO.CopyIn(rw.Ctx, rw.Addr, dst, rw.Opts) 158 end, ok := rw.Addr.AddLength(uint64(n)) 159 if ok { 160 rw.Addr = end 161 } else { 162 // Disallow wraparound. 163 rw.Addr = ^hostarch.Addr(0) 164 if err != nil { 165 err = linuxerr.EFAULT 166 } 167 } 168 return n, err 169 } 170 171 // Write implements io.Writer.Write. 172 func (rw *IOReadWriter) Write(src []byte) (int, error) { 173 n, err := rw.IO.CopyOut(rw.Ctx, rw.Addr, src, rw.Opts) 174 end, ok := rw.Addr.AddLength(uint64(n)) 175 if ok { 176 rw.Addr = end 177 } else { 178 // Disallow wraparound. 179 rw.Addr = ^hostarch.Addr(0) 180 if err != nil { 181 err = linuxerr.EFAULT 182 } 183 } 184 return n, err 185 } 186 187 // CopyStringIn tuning parameters, defined outside that function for tests. 188 const ( 189 copyStringIncrement = 64 190 copyStringMaxInitBufLen = 256 191 ) 192 193 // CopyStringIn copies a NUL-terminated string of unknown length from the 194 // memory mapped at addr in uio and returns it as a string (not including the 195 // trailing NUL). If the length of the string, including the terminating NUL, 196 // would exceed maxlen, CopyStringIn returns the string truncated to maxlen and 197 // ENAMETOOLONG. 198 // 199 // Preconditions: Same as IO.CopyFromUser, plus: 200 // - maxlen >= 0. 201 func CopyStringIn(ctx context.Context, uio IO, addr hostarch.Addr, maxlen int, opts IOOpts) (string, error) { 202 initLen := maxlen 203 if initLen > copyStringMaxInitBufLen { 204 initLen = copyStringMaxInitBufLen 205 } 206 buf := make([]byte, initLen) 207 var done int 208 for done < maxlen { 209 // Read up to copyStringIncrement bytes at a time. 210 readlen := copyStringIncrement 211 if readlen > maxlen-done { 212 readlen = maxlen - done 213 } 214 end, ok := addr.AddLength(uint64(readlen)) 215 if !ok { 216 return gohacks.StringFromImmutableBytes(buf[:done]), linuxerr.EFAULT 217 } 218 // Shorten the read to avoid crossing page boundaries, since faulting 219 // in a page unnecessarily is expensive. This also ensures that partial 220 // copies up to the end of application-mappable memory succeed. 221 if addr.RoundDown() != end.RoundDown() { 222 end = end.RoundDown() 223 readlen = int(end - addr) 224 } 225 // Ensure that our buffer is large enough to accommodate the read. 226 if done+readlen > len(buf) { 227 newBufLen := len(buf) * 2 228 if newBufLen > maxlen { 229 newBufLen = maxlen 230 } 231 buf = append(buf, make([]byte, newBufLen-len(buf))...) 232 } 233 n, err := uio.CopyIn(ctx, addr, buf[done:done+readlen], opts) 234 // Look for the terminating zero byte, which may have occurred before 235 // hitting err. 236 if i := bytes.IndexByte(buf[done:done+n], byte(0)); i >= 0 { 237 return gohacks.StringFromImmutableBytes(buf[:done+i]), nil 238 } 239 240 done += n 241 if err != nil { 242 return gohacks.StringFromImmutableBytes(buf[:done]), err 243 } 244 addr = end 245 } 246 return gohacks.StringFromImmutableBytes(buf), linuxerr.ENAMETOOLONG 247 } 248 249 // CopyOutVec copies bytes from src to the memory mapped at ars in uio. The 250 // maximum number of bytes copied is ars.NumBytes() or len(src), whichever is 251 // less. CopyOutVec returns the number of bytes copied; if this is less than 252 // the maximum, it returns a non-nil error explaining why. 253 // 254 // Preconditions: Same as IO.CopyOut. 255 func CopyOutVec(ctx context.Context, uio IO, ars hostarch.AddrRangeSeq, src []byte, opts IOOpts) (int, error) { 256 var done int 257 for !ars.IsEmpty() && done < len(src) { 258 ar := ars.Head() 259 cplen := len(src) - done 260 if hostarch.Addr(cplen) >= ar.Length() { 261 cplen = int(ar.Length()) 262 } 263 n, err := uio.CopyOut(ctx, ar.Start, src[done:done+cplen], opts) 264 done += n 265 if err != nil { 266 return done, err 267 } 268 ars = ars.DropFirst(n) 269 } 270 return done, nil 271 } 272 273 // CopyInVec copies bytes from the memory mapped at ars in uio to dst. The 274 // maximum number of bytes copied is ars.NumBytes() or len(dst), whichever is 275 // less. CopyInVec returns the number of bytes copied; if this is less than the 276 // maximum, it returns a non-nil error explaining why. 277 // 278 // Preconditions: Same as IO.CopyIn. 279 func CopyInVec(ctx context.Context, uio IO, ars hostarch.AddrRangeSeq, dst []byte, opts IOOpts) (int, error) { 280 var done int 281 for !ars.IsEmpty() && done < len(dst) { 282 ar := ars.Head() 283 cplen := len(dst) - done 284 if hostarch.Addr(cplen) >= ar.Length() { 285 cplen = int(ar.Length()) 286 } 287 n, err := uio.CopyIn(ctx, ar.Start, dst[done:done+cplen], opts) 288 done += n 289 if err != nil { 290 return done, err 291 } 292 ars = ars.DropFirst(n) 293 } 294 return done, nil 295 } 296 297 // ZeroOutVec writes zeroes to the memory mapped at ars in uio. The maximum 298 // number of bytes written is ars.NumBytes() or toZero, whichever is less. 299 // ZeroOutVec returns the number of bytes written; if this is less than the 300 // maximum, it returns a non-nil error explaining why. 301 // 302 // Preconditions: Same as IO.ZeroOut. 303 func ZeroOutVec(ctx context.Context, uio IO, ars hostarch.AddrRangeSeq, toZero int64, opts IOOpts) (int64, error) { 304 var done int64 305 for !ars.IsEmpty() && done < toZero { 306 ar := ars.Head() 307 cplen := toZero - done 308 if hostarch.Addr(cplen) >= ar.Length() { 309 cplen = int64(ar.Length()) 310 } 311 n, err := uio.ZeroOut(ctx, ar.Start, cplen, opts) 312 done += n 313 if err != nil { 314 return done, err 315 } 316 ars = ars.DropFirst64(n) 317 } 318 return done, nil 319 } 320 321 func isASCIIWhitespace(b byte) bool { 322 // Compare Linux include/linux/ctype.h, lib/ctype.c. 323 // 9 => horizontal tab '\t' 324 // 10 => line feed '\n' 325 // 11 => vertical tab '\v' 326 // 12 => form feed '\c' 327 // 13 => carriage return '\r' 328 return b == ' ' || (b >= 9 && b <= 13) 329 } 330 331 // CopyInt32StringsInVec copies up to len(dsts) whitespace-separated decimal 332 // strings from the memory mapped at ars in uio and converts them to int32 333 // values in dsts. It returns the number of bytes read. 334 // 335 // CopyInt32StringsInVec shares the following properties with Linux's 336 // kernel/sysctl.c:proc_dointvec(write=1): 337 // 338 // - If any read value overflows the range of int32, or any invalid characters 339 // are encountered during the read, CopyInt32StringsInVec returns EINVAL. 340 // 341 // - If, upon reaching the end of ars, fewer than len(dsts) values have been 342 // read, CopyInt32StringsInVec returns no error if at least 1 value was read 343 // and EINVAL otherwise. 344 // 345 // - Trailing whitespace after the last successfully read value is counted in 346 // the number of bytes read. 347 // 348 // Unlike proc_dointvec(): 349 // 350 // - CopyInt32StringsInVec does not implicitly limit ars.NumBytes() to 351 // PageSize-1; callers that require this must do so explicitly. 352 // 353 // - CopyInt32StringsInVec returns EINVAL if ars.NumBytes() == 0. 354 // 355 // Preconditions: Same as CopyInVec. 356 func CopyInt32StringsInVec(ctx context.Context, uio IO, ars hostarch.AddrRangeSeq, dsts []int32, opts IOOpts) (int64, error) { 357 if len(dsts) == 0 { 358 return 0, nil 359 } 360 361 buf := make([]byte, ars.NumBytes()) 362 n, cperr := CopyInVec(ctx, uio, ars, buf, opts) 363 buf = buf[:n] 364 365 var i, j int 366 for ; j < len(dsts); j++ { 367 // Skip leading whitespace. 368 for i < len(buf) && isASCIIWhitespace(buf[i]) { 369 i++ 370 } 371 if i == len(buf) { 372 break 373 } 374 375 // Find the end of the value to be parsed (next whitespace or end of string). 376 nextI := i + 1 377 for nextI < len(buf) && !isASCIIWhitespace(buf[nextI]) { 378 nextI++ 379 } 380 381 // Parse a single value. 382 val, err := strconv.ParseInt(string(buf[i:nextI]), 10, 32) 383 if err != nil { 384 return int64(i), linuxerr.EINVAL 385 } 386 dsts[j] = int32(val) 387 388 i = nextI 389 } 390 391 // Skip trailing whitespace. 392 for i < len(buf) && isASCIIWhitespace(buf[i]) { 393 i++ 394 } 395 396 if cperr != nil { 397 return int64(i), cperr 398 } 399 if j == 0 { 400 return int64(i), linuxerr.EINVAL 401 } 402 return int64(i), nil 403 } 404 405 // CopyInt32StringInVec is equivalent to CopyInt32StringsInVec, but copies at 406 // most one int32. 407 func CopyInt32StringInVec(ctx context.Context, uio IO, ars hostarch.AddrRangeSeq, dst *int32, opts IOOpts) (int64, error) { 408 dsts := [1]int32{*dst} 409 n, err := CopyInt32StringsInVec(ctx, uio, ars, dsts[:], opts) 410 *dst = dsts[0] 411 return n, err 412 } 413 414 // IOSequence holds arguments to IO methods. 415 type IOSequence struct { 416 IO IO 417 Addrs hostarch.AddrRangeSeq 418 Opts IOOpts 419 } 420 421 // NumBytes returns s.Addrs.NumBytes(). 422 // 423 // Note that NumBytes() may return 0 even if !s.Addrs.IsEmpty(), since 424 // s.Addrs may contain a non-zero number of zero-length AddrRanges. 425 // Many clients of 426 // IOSequence currently do something like: 427 // 428 // if ioseq.NumBytes() == 0 { 429 // return 0, nil 430 // } 431 // if f.availableBytes == 0 { 432 // return 0, linuxerr.ErrWouldBlock 433 // } 434 // return ioseq.CopyOutFrom(..., reader) 435 // 436 // In such cases, using s.Addrs.IsEmpty() will cause them to have the wrong 437 // behavior for zero-length I/O. However, using s.NumBytes() == 0 instead means 438 // that we will return success for zero-length I/O in cases where Linux would 439 // return EFAULT due to a failed access_ok() check, so in the long term we 440 // should move checks for ErrWouldBlock etc. into the body of 441 // reader.ReadToBlocks and use s.Addrs.IsEmpty() instead. 442 func (s IOSequence) NumBytes() int64 { 443 return s.Addrs.NumBytes() 444 } 445 446 // DropFirst returns a copy of s with s.Addrs.DropFirst(n). 447 // 448 // Preconditions: Same as hostarch.AddrRangeSeq.DropFirst. 449 func (s IOSequence) DropFirst(n int) IOSequence { 450 return IOSequence{s.IO, s.Addrs.DropFirst(n), s.Opts} 451 } 452 453 // DropFirst64 returns a copy of s with s.Addrs.DropFirst64(n). 454 // 455 // Preconditions: Same as hostarch.AddrRangeSeq.DropFirst64. 456 func (s IOSequence) DropFirst64(n int64) IOSequence { 457 return IOSequence{s.IO, s.Addrs.DropFirst64(n), s.Opts} 458 } 459 460 // TakeFirst returns a copy of s with s.Addrs.TakeFirst(n). 461 // 462 // Preconditions: Same as hostarch.AddrRangeSeq.TakeFirst. 463 func (s IOSequence) TakeFirst(n int) IOSequence { 464 return IOSequence{s.IO, s.Addrs.TakeFirst(n), s.Opts} 465 } 466 467 // TakeFirst64 returns a copy of s with s.Addrs.TakeFirst64(n). 468 // 469 // Preconditions: Same as hostarch.AddrRangeSeq.TakeFirst64. 470 func (s IOSequence) TakeFirst64(n int64) IOSequence { 471 return IOSequence{s.IO, s.Addrs.TakeFirst64(n), s.Opts} 472 } 473 474 // CopyOut invokes CopyOutVec over s.Addrs. 475 // 476 // As with CopyOutVec, if s.NumBytes() < len(src), the copy will be truncated 477 // to s.NumBytes(), and a nil error will be returned. 478 // 479 // Preconditions: Same as CopyOutVec. 480 func (s IOSequence) CopyOut(ctx context.Context, src []byte) (int, error) { 481 return CopyOutVec(ctx, s.IO, s.Addrs, src, s.Opts) 482 } 483 484 // CopyIn invokes CopyInVec over s.Addrs. 485 // 486 // As with CopyInVec, if s.NumBytes() < len(dst), the copy will be truncated to 487 // s.NumBytes(), and a nil error will be returned. 488 // 489 // Preconditions: Same as CopyInVec. 490 func (s IOSequence) CopyIn(ctx context.Context, dst []byte) (int, error) { 491 return CopyInVec(ctx, s.IO, s.Addrs, dst, s.Opts) 492 } 493 494 // ZeroOut invokes ZeroOutVec over s.Addrs. 495 // 496 // As with ZeroOutVec, if s.NumBytes() < toZero, the write will be truncated 497 // to s.NumBytes(), and a nil error will be returned. 498 // 499 // Preconditions: Same as ZeroOutVec. 500 func (s IOSequence) ZeroOut(ctx context.Context, toZero int64) (int64, error) { 501 return ZeroOutVec(ctx, s.IO, s.Addrs, toZero, s.Opts) 502 } 503 504 // CopyOutFrom invokes s.CopyOutFrom over s.Addrs. 505 // 506 // Preconditions: Same as IO.CopyOutFrom. 507 func (s IOSequence) CopyOutFrom(ctx context.Context, src safemem.Reader) (int64, error) { 508 return s.IO.CopyOutFrom(ctx, s.Addrs, src, s.Opts) 509 } 510 511 // CopyInTo invokes s.CopyInTo over s.Addrs. 512 // 513 // Preconditions: Same as IO.CopyInTo. 514 func (s IOSequence) CopyInTo(ctx context.Context, dst safemem.Writer) (int64, error) { 515 return s.IO.CopyInTo(ctx, s.Addrs, dst, s.Opts) 516 } 517 518 // Reader returns an io.Reader that reads from s. Reads beyond the end of s 519 // return io.EOF. The preconditions that apply to s.CopyIn also apply to the 520 // returned io.Reader.Read. 521 func (s IOSequence) Reader(ctx context.Context) *IOSequenceReadWriter { 522 return &IOSequenceReadWriter{ctx, s} 523 } 524 525 // Writer returns an io.Writer that writes to s. Writes beyond the end of s 526 // return ErrEndOfIOSequence. The preconditions that apply to s.CopyOut also 527 // apply to the returned io.Writer.Write. 528 func (s IOSequence) Writer(ctx context.Context) *IOSequenceReadWriter { 529 return &IOSequenceReadWriter{ctx, s} 530 } 531 532 // ErrEndOfIOSequence is returned by IOSequence.Writer().Write() when 533 // attempting to write beyond the end of the IOSequence. 534 var ErrEndOfIOSequence = errors.New("write beyond end of IOSequence") 535 536 // IOSequenceReadWriter implements io.Reader and io.Writer for an IOSequence. 537 type IOSequenceReadWriter struct { 538 ctx context.Context 539 s IOSequence 540 } 541 542 // Read implements io.Reader.Read. 543 func (rw *IOSequenceReadWriter) Read(dst []byte) (int, error) { 544 n, err := rw.s.CopyIn(rw.ctx, dst) 545 rw.s = rw.s.DropFirst(n) 546 if err == nil && rw.s.NumBytes() == 0 { 547 err = io.EOF 548 } 549 return n, err 550 } 551 552 // Len implements tcpip.Payloader. 553 func (rw *IOSequenceReadWriter) Len() int { 554 return int(rw.s.NumBytes()) 555 } 556 557 // Write implements io.Writer.Write. 558 func (rw *IOSequenceReadWriter) Write(src []byte) (int, error) { 559 n, err := rw.s.CopyOut(rw.ctx, src) 560 rw.s = rw.s.DropFirst(n) 561 if err == nil && n < len(src) { 562 err = ErrEndOfIOSequence 563 } 564 return n, err 565 }