github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/syscalls/linux/error.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 linux
    16  
    17  import (
    18  	"io"
    19  
    20  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    21  	"github.com/SagerNet/gvisor/pkg/context"
    22  	"github.com/SagerNet/gvisor/pkg/errors/linuxerr"
    23  	"github.com/SagerNet/gvisor/pkg/log"
    24  	"github.com/SagerNet/gvisor/pkg/metric"
    25  	"github.com/SagerNet/gvisor/pkg/sentry/fs"
    26  	"github.com/SagerNet/gvisor/pkg/sentry/kernel"
    27  	"github.com/SagerNet/gvisor/pkg/sentry/vfs"
    28  	"github.com/SagerNet/gvisor/pkg/sync"
    29  	"github.com/SagerNet/gvisor/pkg/syserror"
    30  )
    31  
    32  var (
    33  	partialResultOnce sync.Once
    34  )
    35  
    36  // incrementPartialResultMetric increments PartialResultMetric by calling
    37  // Increment(). This is added as the func Do() which is called below requires
    38  // us to pass a function which does not take any arguments, whereas Increment()
    39  // takes a variadic number of arguments.
    40  func incrementPartialResultMetric() {
    41  	metric.WeirdnessMetric.Increment("partial_result")
    42  }
    43  
    44  // HandleIOErrorVFS2 handles special error cases for partial results. For some
    45  // errors, we may consume the error and return only the partial read/write.
    46  //
    47  // op and f are used only for panics.
    48  func HandleIOErrorVFS2(ctx context.Context, partialResult bool, ioerr, intr error, op string, f *vfs.FileDescription) error {
    49  	known, err := handleIOErrorImpl(ctx, partialResult, ioerr, intr, op)
    50  	if err != nil {
    51  		return err
    52  	}
    53  	if !known {
    54  		// An unknown error is encountered with a partial read/write.
    55  		fs := f.Mount().Filesystem().VirtualFilesystem()
    56  		root := vfs.RootFromContext(ctx)
    57  		name, _ := fs.PathnameWithDeleted(ctx, root, f.VirtualDentry())
    58  		log.Traceback("Invalid request partialResult %v and err (type %T) %v for %s operation on %q", partialResult, ioerr, ioerr, op, name)
    59  		partialResultOnce.Do(incrementPartialResultMetric)
    60  	}
    61  	return nil
    62  }
    63  
    64  // handleIOError handles special error cases for partial results. For some
    65  // errors, we may consume the error and return only the partial read/write.
    66  //
    67  // op and f are used only for panics.
    68  func handleIOError(ctx context.Context, partialResult bool, ioerr, intr error, op string, f *fs.File) error {
    69  	known, err := handleIOErrorImpl(ctx, partialResult, ioerr, intr, op)
    70  	if err != nil {
    71  		return err
    72  	}
    73  	if !known {
    74  		// An unknown error is encountered with a partial read/write.
    75  		name, _ := f.Dirent.FullName(nil /* ignore chroot */)
    76  		log.Traceback("Invalid request partialResult %v and err (type %T) %v for %s operation on %q, %T", partialResult, ioerr, ioerr, op, name, f.FileOperations)
    77  		partialResultOnce.Do(incrementPartialResultMetric)
    78  	}
    79  	return nil
    80  }
    81  
    82  // handleIOError handles special error cases for partial results. For some
    83  // errors, we may consume the error and return only the partial read/write.
    84  //
    85  // Returns false if error is unknown.
    86  func handleIOErrorImpl(ctx context.Context, partialResult bool, errOrig, intr error, op string) (bool, error) {
    87  	if errOrig == nil {
    88  		// Typical successful syscall.
    89  		return true, nil
    90  	}
    91  
    92  	// Translate error, if possible, to consolidate errors from other packages
    93  	// into a smaller set of errors from syserror package.
    94  	translatedErr := errOrig
    95  	if errno, ok := syserror.TranslateError(errOrig); ok {
    96  		translatedErr = errno
    97  	}
    98  	switch {
    99  	case translatedErr == io.EOF:
   100  		// EOF is always consumed. If this is a partial read/write
   101  		// (result != 0), the application will see that, otherwise
   102  		// they will see 0.
   103  		return true, nil
   104  	case linuxerr.Equals(linuxerr.EFBIG, translatedErr):
   105  		t := kernel.TaskFromContext(ctx)
   106  		if t == nil {
   107  			panic("I/O error should only occur from a context associated with a Task")
   108  		}
   109  		// Ignore partialResult because this error only applies to
   110  		// normal files, and for those files we cannot accumulate
   111  		// write results.
   112  		//
   113  		// Do not consume the error and return it as EFBIG.
   114  		// Simultaneously send a SIGXFSZ per setrlimit(2).
   115  		t.SendSignal(kernel.SignalInfoNoInfo(linux.SIGXFSZ, t, t))
   116  		return true, linuxerr.EFBIG
   117  	case linuxerr.Equals(linuxerr.EINTR, translatedErr):
   118  		// The syscall was interrupted. Return nil if it completed
   119  		// partially, otherwise return the error code that the syscall
   120  		// needs (to indicate to the kernel what it should do).
   121  		if partialResult {
   122  			return true, nil
   123  		}
   124  		return true, intr
   125  	}
   126  
   127  	if !partialResult {
   128  		// Typical syscall error.
   129  		return true, errOrig
   130  	}
   131  
   132  	switch {
   133  	case linuxerr.Equals(linuxerr.EINTR, translatedErr):
   134  		// Syscall interrupted, but completed a partial
   135  		// read/write.  Like ErrWouldBlock, since we have a
   136  		// partial read/write, we consume the error and return
   137  		// the partial result.
   138  		return true, nil
   139  	case linuxerr.Equals(linuxerr.EFAULT, translatedErr):
   140  		// EFAULT is only shown the user if nothing was
   141  		// read/written. If we read something (this case), they see
   142  		// a partial read/write. They will then presumably try again
   143  		// with an incremented buffer, which will EFAULT with
   144  		// result == 0.
   145  		return true, nil
   146  	case linuxerr.Equals(linuxerr.EPIPE, translatedErr):
   147  		// Writes to a pipe or socket will return EPIPE if the other
   148  		// side is gone. The partial write is returned. EPIPE will be
   149  		// returned on the next call.
   150  		//
   151  		// TODO(github.com/SagerNet/issue/161): In some cases SIGPIPE should
   152  		// also be sent to the application.
   153  		return true, nil
   154  	case linuxerr.Equals(linuxerr.ENOSPC, translatedErr):
   155  		// Similar to EPIPE. Return what we wrote this time, and let
   156  		// ENOSPC be returned on the next call.
   157  		return true, nil
   158  	case linuxerr.Equals(linuxerr.ECONNRESET, translatedErr):
   159  		fallthrough
   160  	case linuxerr.Equals(linuxerr.ETIMEDOUT, translatedErr):
   161  		// For TCP sendfile connections, we may have a reset or timeout. But we
   162  		// should just return n as the result.
   163  		return true, nil
   164  	case linuxerr.Equals(linuxerr.EWOULDBLOCK, translatedErr):
   165  		// Syscall would block, but completed a partial read/write.
   166  		// This case should only be returned by IssueIO for nonblocking
   167  		// files. Since we have a partial read/write, we consume
   168  		// ErrWouldBlock, returning the partial result.
   169  		return true, nil
   170  	}
   171  
   172  	switch errOrig.(type) {
   173  	case syserror.SyscallRestartErrno:
   174  		// Identical to the EINTR case.
   175  		return true, nil
   176  	}
   177  
   178  	// Error is unknown and cannot be properly handled.
   179  	return false, nil
   180  }