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 }