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  }