mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Fix error handling on fusefs mount.
Don't propagate arbitrary golang errors up from fusefs because errors that don't map to an errno result in a sentry panic. Reported-by: syzbot+697cb635346e456fddfc@syzkaller.appspotmail.com PiperOrigin-RevId: 346220306
This commit is contained in:
committed by
gVisor bot
parent
1aeca3bfb7
commit
9c198e5df4
@@ -84,11 +84,7 @@ func (conn *connection) InitSend(creds *auth.Credentials, pid uint32) error {
|
||||
Flags: fuseDefaultInitFlags,
|
||||
}
|
||||
|
||||
req, err := conn.NewRequest(creds, pid, 0, linux.FUSE_INIT, &in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := conn.NewRequest(creds, pid, 0, linux.FUSE_INIT, &in)
|
||||
// Since there is no task to block on and FUSE_INIT is the request
|
||||
// to unblock other requests, use nil.
|
||||
return conn.CallAsync(nil, req)
|
||||
|
||||
@@ -76,10 +76,7 @@ func TestConnectionAbort(t *testing.T) {
|
||||
var futNormal []*futureResponse
|
||||
|
||||
for i := 0; i < int(numRequests); i++ {
|
||||
req, err := conn.NewRequest(creds, uint32(i), uint64(i), 0, testObj)
|
||||
if err != nil {
|
||||
t.Fatalf("NewRequest creation failed: %v", err)
|
||||
}
|
||||
req := conn.NewRequest(creds, uint32(i), uint64(i), 0, testObj)
|
||||
fut, err := conn.callFutureLocked(task, req)
|
||||
if err != nil {
|
||||
t.Fatalf("callFutureLocked failed: %v", err)
|
||||
@@ -105,10 +102,7 @@ func TestConnectionAbort(t *testing.T) {
|
||||
}
|
||||
|
||||
// After abort, Call() should return directly with ENOTCONN.
|
||||
req, err := conn.NewRequest(creds, 0, 0, 0, testObj)
|
||||
if err != nil {
|
||||
t.Fatalf("NewRequest creation failed: %v", err)
|
||||
}
|
||||
req := conn.NewRequest(creds, 0, 0, 0, testObj)
|
||||
_, err = conn.Call(task, req)
|
||||
if err != syserror.ENOTCONN {
|
||||
t.Fatalf("Incorrect error code received for Call() after connection aborted")
|
||||
|
||||
@@ -219,10 +219,7 @@ func fuseClientRun(t *testing.T, s *testutil.System, k *kernel.Kernel, conn *con
|
||||
data: rand.Uint32(),
|
||||
}
|
||||
|
||||
req, err := conn.NewRequest(creds, pid, inode, echoTestOpcode, testObj)
|
||||
if err != nil {
|
||||
t.Fatalf("NewRequest creation failed: %v", err)
|
||||
}
|
||||
req := conn.NewRequest(creds, pid, inode, echoTestOpcode, testObj)
|
||||
|
||||
// Queue up a request.
|
||||
// Analogous to Call except it doesn't block on the task.
|
||||
|
||||
@@ -68,11 +68,7 @@ func (dir *directoryFD) IterDirents(ctx context.Context, callback vfs.IterDirent
|
||||
}
|
||||
|
||||
// TODO(gVisor.dev/issue/3404): Support FUSE_READDIRPLUS.
|
||||
req, err := fusefs.conn.NewRequest(creds, uint32(task.ThreadID()), dir.inode().nodeID, linux.FUSE_READDIR, &in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := fusefs.conn.NewRequest(creds, uint32(task.ThreadID()), dir.inode().nodeID, linux.FUSE_READDIR, &in)
|
||||
res, err := fusefs.conn.Call(task, req)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -83,12 +83,8 @@ func (fd *fileDescription) Release(ctx context.Context) {
|
||||
opcode = linux.FUSE_RELEASE
|
||||
}
|
||||
kernelTask := kernel.TaskFromContext(ctx)
|
||||
// ignoring errors and FUSE server reply is analogous to Linux's behavior.
|
||||
req, err := conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), fd.inode().nodeID, opcode, &in)
|
||||
if err != nil {
|
||||
// No way to invoke Call() with an errored request.
|
||||
return
|
||||
}
|
||||
// Ignoring errors and FUSE server reply is analogous to Linux's behavior.
|
||||
req := conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), fd.inode().nodeID, opcode, &in)
|
||||
// The reply will be ignored since no callback is defined in asyncCallBack().
|
||||
conn.CallAsync(kernelTask, req)
|
||||
}
|
||||
|
||||
@@ -119,7 +119,8 @@ func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
|
||||
|
||||
deviceDescriptor, err := strconv.ParseInt(deviceDescriptorStr, 10 /* base */, 32 /* bitSize */)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
log.Debugf("%s.GetFilesystem: device FD '%v' not parsable: %v", fsType.Name(), deviceDescriptorStr, err)
|
||||
return nil, nil, syserror.EINVAL
|
||||
}
|
||||
|
||||
kernelTask := kernel.TaskFromContext(ctx)
|
||||
@@ -360,12 +361,8 @@ func (i *inode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.Dentr
|
||||
in.Flags &= ^uint32(linux.O_TRUNC)
|
||||
}
|
||||
|
||||
req, err := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), i.nodeID, opcode, &in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Send the request and receive the reply.
|
||||
req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), i.nodeID, opcode, &in)
|
||||
res, err := i.fs.conn.Call(kernelTask, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -485,10 +482,7 @@ func (i *inode) Unlink(ctx context.Context, name string, child kernfs.Inode) err
|
||||
return syserror.EINVAL
|
||||
}
|
||||
in := linux.FUSEUnlinkIn{Name: name}
|
||||
req, err := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), i.nodeID, linux.FUSE_UNLINK, &in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), i.nodeID, linux.FUSE_UNLINK, &in)
|
||||
res, err := i.fs.conn.Call(kernelTask, req)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -515,11 +509,7 @@ func (i *inode) RmDir(ctx context.Context, name string, child kernfs.Inode) erro
|
||||
task, creds := kernel.TaskFromContext(ctx), auth.CredentialsFromContext(ctx)
|
||||
|
||||
in := linux.FUSERmDirIn{Name: name}
|
||||
req, err := fusefs.conn.NewRequest(creds, uint32(task.ThreadID()), i.nodeID, linux.FUSE_RMDIR, &in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := fusefs.conn.NewRequest(creds, uint32(task.ThreadID()), i.nodeID, linux.FUSE_RMDIR, &in)
|
||||
res, err := i.fs.conn.Call(task, req)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -535,10 +525,7 @@ func (i *inode) newEntry(ctx context.Context, name string, fileType linux.FileMo
|
||||
log.Warningf("fusefs.Inode.newEntry: couldn't get kernel task from context", i.nodeID)
|
||||
return nil, syserror.EINVAL
|
||||
}
|
||||
req, err := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), i.nodeID, opcode, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), i.nodeID, opcode, payload)
|
||||
res, err := i.fs.conn.Call(kernelTask, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -574,10 +561,7 @@ func (i *inode) Readlink(ctx context.Context, mnt *vfs.Mount) (string, error) {
|
||||
log.Warningf("fusefs.Inode.Readlink: couldn't get kernel task from context")
|
||||
return "", syserror.EINVAL
|
||||
}
|
||||
req, err := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), i.nodeID, linux.FUSE_READLINK, &linux.FUSEEmptyIn{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), i.nodeID, linux.FUSE_READLINK, &linux.FUSEEmptyIn{})
|
||||
res, err := i.fs.conn.Call(kernelTask, req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -680,11 +664,7 @@ func (i *inode) getAttr(ctx context.Context, fs *vfs.Filesystem, opts vfs.StatOp
|
||||
GetAttrFlags: flags,
|
||||
Fh: fh,
|
||||
}
|
||||
req, err := i.fs.conn.NewRequest(creds, uint32(task.ThreadID()), i.nodeID, linux.FUSE_GETATTR, &in)
|
||||
if err != nil {
|
||||
return linux.FUSEAttr{}, err
|
||||
}
|
||||
|
||||
req := i.fs.conn.NewRequest(creds, uint32(task.ThreadID()), i.nodeID, linux.FUSE_GETATTR, &in)
|
||||
res, err := i.fs.conn.Call(task, req)
|
||||
if err != nil {
|
||||
return linux.FUSEAttr{}, err
|
||||
@@ -803,11 +783,7 @@ func (i *inode) setAttr(ctx context.Context, fs *vfs.Filesystem, creds *auth.Cre
|
||||
UID: opts.Stat.UID,
|
||||
GID: opts.Stat.GID,
|
||||
}
|
||||
req, err := conn.NewRequest(creds, uint32(task.ThreadID()), i.nodeID, linux.FUSE_SETATTR, &in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := conn.NewRequest(creds, uint32(task.ThreadID()), i.nodeID, linux.FUSE_SETATTR, &in)
|
||||
res, err := conn.Call(task, req)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -79,13 +79,9 @@ func (fs *filesystem) ReadInPages(ctx context.Context, fd *regularFileFD, off ui
|
||||
in.Offset = off + (uint64(pagesRead) << usermem.PageShift)
|
||||
in.Size = pagesCanRead << usermem.PageShift
|
||||
|
||||
req, err := fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(t.ThreadID()), fd.inode().nodeID, linux.FUSE_READ, &in)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// TODO(gvisor.dev/issue/3247): support async read.
|
||||
|
||||
req := fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(t.ThreadID()), fd.inode().nodeID, linux.FUSE_READ, &in)
|
||||
res, err := fs.conn.Call(t, req)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
@@ -204,11 +200,7 @@ func (fs *filesystem) Write(ctx context.Context, fd *regularFileFD, off uint64,
|
||||
in.Offset = off + uint64(written)
|
||||
in.Size = toWrite
|
||||
|
||||
req, err := fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(t.ThreadID()), inode.nodeID, linux.FUSE_WRITE, &in)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
req := fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(t.ThreadID()), inode.nodeID, linux.FUSE_WRITE, &in)
|
||||
req.payload = data[written : written+toWrite]
|
||||
|
||||
// TODO(gvisor.dev/issue/3247): support async write.
|
||||
|
||||
@@ -104,7 +104,7 @@ type Request struct {
|
||||
}
|
||||
|
||||
// NewRequest creates a new request that can be sent to the FUSE server.
|
||||
func (conn *connection) NewRequest(creds *auth.Credentials, pid uint32, ino uint64, opcode linux.FUSEOpcode, payload marshal.Marshallable) (*Request, error) {
|
||||
func (conn *connection) NewRequest(creds *auth.Credentials, pid uint32, ino uint64, opcode linux.FUSEOpcode, payload marshal.Marshallable) *Request {
|
||||
conn.fd.mu.Lock()
|
||||
defer conn.fd.mu.Unlock()
|
||||
conn.fd.nextOpID += linux.FUSEOpID(reqIDStep)
|
||||
@@ -130,7 +130,7 @@ func (conn *connection) NewRequest(creds *auth.Credentials, pid uint32, ino uint
|
||||
id: hdr.Unique,
|
||||
hdr: &hdr,
|
||||
data: buf,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// futureResponse represents an in-flight request, that may or may not have
|
||||
|
||||
@@ -71,3 +71,8 @@ syscall_test(
|
||||
fuse = "True",
|
||||
test = "//test/fuse/linux:setstat_test",
|
||||
)
|
||||
|
||||
syscall_test(
|
||||
fuse = "True",
|
||||
test = "//test/fuse/linux:mount_test",
|
||||
)
|
||||
|
||||
@@ -228,3 +228,15 @@ cc_binary(
|
||||
"//test/util:test_util",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "mount_test",
|
||||
testonly = 1,
|
||||
srcs = ["mount_test.cc"],
|
||||
deps = [
|
||||
gtest,
|
||||
"//test/util:temp_path",
|
||||
"//test/util:test_main",
|
||||
"//test/util:test_util",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/util/temp_path.h"
|
||||
#include "test/util/test_util.h"
|
||||
|
||||
namespace gvisor {
|
||||
namespace testing {
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(FuseMount, FDNotParsable) {
|
||||
int devfd;
|
||||
EXPECT_THAT(devfd = open("/dev/fuse", O_RDWR), SyscallSucceeds());
|
||||
std::string mount_opts = "fd=thiscantbeparsed";
|
||||
TempPath mount_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
|
||||
EXPECT_THAT(mount("fuse", mount_dir.path().c_str(), "fuse",
|
||||
MS_NODEV | MS_NOSUID, mount_opts.c_str()),
|
||||
SyscallFailsWithErrno(EINVAL));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace testing
|
||||
} // namespace gvisor
|
||||
Reference in New Issue
Block a user