From b1ade52f24be029dfeb193b4ca3ec1d53e8391f2 Mon Sep 17 00:00:00 2001 From: Kevin Krakauer Date: Wed, 7 Aug 2024 00:53:49 -0700 Subject: [PATCH] fuse: handle bad response errors FUSE is supposed to receive an error code corresponding to Linux errors, but since it's running in another process we can't guarantee that. So instead of propogating a nonsensical error code (that can lead, in some cases, to panics), warn and convert the failure to EINVAL. PiperOrigin-RevId: 660274364 --- pkg/errors/linuxerr/linuxerr.go | 11 ++++++++--- pkg/sentry/fsimpl/fuse/request_response.go | 6 +++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/pkg/errors/linuxerr/linuxerr.go b/pkg/errors/linuxerr/linuxerr.go index e317844db..eff0a621f 100644 --- a/pkg/errors/linuxerr/linuxerr.go +++ b/pkg/errors/linuxerr/linuxerr.go @@ -13,7 +13,7 @@ // limitations under the License. // Package linuxerr contains syscall error codes exported as an error interface -// pointers. This allows for fast comparison and return operations comperable +// pointers. This allows for fast comparison and return operations comparable // to unix.Errno constants. package linuxerr @@ -29,7 +29,7 @@ const maxErrno uint32 = errno.EHWPOISON + 1 // The following errors are semantically identical to Errno of type unix.Errno // or sycall.Errno. However, since the type are distinct ( these are -// *errors.Error), they are not directly comperable. However, the Errno method +// *errors.Error), they are not directly comparable. However, the Errno method // returns an Errno number such that the error can be compared to unix/syscall.Errno // (e.g. unix.Errno(EPERM.Errno()) == unix.EPERM is true). Converting unix/syscall.Errno // to the errors should be done via the lookup methods provided. @@ -355,7 +355,7 @@ func ToUnix(e *errors.Error) unix.Errno { return unixErr } -// Equals compars a linuxerr to a given error. +// Equals compares a linuxerr to a given error. func Equals(e *errors.Error, err error) bool { var unixErr unix.Errno if e != noError { @@ -366,3 +366,8 @@ func Equals(e *errors.Error, err error) bool { } return e == err || unixErr == err } + +// IsValid returns whether err is a valid error number. +func IsValid(errno unix.Errno) bool { + return errno < unix.Errno(maxErrno) +} diff --git a/pkg/sentry/fsimpl/fuse/request_response.go b/pkg/sentry/fsimpl/fuse/request_response.go index e91d3974c..3e4d4dd58 100644 --- a/pkg/sentry/fsimpl/fuse/request_response.go +++ b/pkg/sentry/fsimpl/fuse/request_response.go @@ -195,7 +195,12 @@ func (r *Response) Error() error { return nil } + // If we get a bad error in the response, warn and convert it to EINVAL. sysErrNo := unix.Errno(-errno) + if !linuxerr.IsValid(sysErrNo) { + log.Warningf("fusefs: invalid response error %d does not correspond to a Linux error", sysErrNo) + sysErrNo = unix.Errno(unix.EINVAL) + } return error(sysErrNo) } @@ -213,7 +218,6 @@ func (r *Response) UnmarshalPayload(m marshal.Marshallable) error { if haveDataLen < wantDataLen { log.Warningf("fusefs: Payload too small. Minimum data length required: %d, but got data length %d", wantDataLen, haveDataLen) return linuxerr.EINVAL - } // The response data is empty unless there is some payload. And so, doesn't