github.com/ttpreport/gvisor-ligolo@v0.0.0-20240123134145-a858404967ba/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/ttpreport/gvisor-ligolo/pkg/abi/linux"
    21  	"github.com/ttpreport/gvisor-ligolo/pkg/context"
    22  	"github.com/ttpreport/gvisor-ligolo/pkg/errors/linuxerr"
    23  	"github.com/ttpreport/gvisor-ligolo/pkg/hostarch"
    24  	"github.com/ttpreport/gvisor-ligolo/pkg/marshal"
    25  	"github.com/ttpreport/gvisor-ligolo/pkg/sentry/mm"
    26  	"github.com/ttpreport/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  }