Page MenuHomeFreeBSD

D31994.id95266.diff
No OneTemporary

D31994.id95266.diff

Index: sys/fs/fuse/fuse_vnops.c
===================================================================
--- sys/fs/fuse/fuse_vnops.c
+++ sys/fs/fuse/fuse_vnops.c
@@ -2206,11 +2206,10 @@
const int biosize = fuse_iosize(vp);
err = fuse_vnode_size(vp, &filesize, NULL, NULL);
- KASSERT(err == 0, ("vfs_bio_getpages can't handle errors here"));
- if (err)
- return biosize;
-
- if ((off_t)lbn * biosize >= filesize) {
+ if (err) {
+ /* Returning -1 effectively causes a SIGBUS */
+ blksz = -1;
+ } else if ((off_t)lbn * biosize >= filesize) {
blksz = 0;
} else if ((off_t)(lbn + 1) * biosize > filesize) {
blksz = filesize - (off_t)lbn *biosize;
Index: sys/kern/vfs_bio.c
===================================================================
--- sys/kern/vfs_bio.c
+++ sys/kern/vfs_bio.c
@@ -5222,6 +5222,8 @@
la += PAGE_SIZE;
lpart = la > object->un_pager.vnp.vnp_size;
bo_bs = get_blksize(vp, get_lblkno(vp, IDX_TO_OFF(ma[0]->pindex)));
+ if (bo_bs < 0)
+ return (VM_PAGER_ERROR);
/*
* Calculate read-ahead, behind and total pages.
@@ -5278,6 +5280,10 @@
lbnp = lbn;
bsize = get_blksize(vp, lbn);
+ if (bsize < 0) {
+ error = 1;
+ goto end_pages;
+ }
error = bread_gb(vp, lbn, bsize, curthread->td_ucred,
br_flags, &bp);
if (error != 0)
Index: tests/sys/fs/fusefs/read.cc
===================================================================
--- tests/sys/fs/fusefs/read.cc
+++ tests/sys/fs/fusefs/read.cc
@@ -584,6 +584,52 @@
leak(fd);
}
+/* Read of an mmap()ed file fails */
+TEST_F(Read, mmap_eio)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t len;
+ size_t bufsize = strlen(CONTENTS);
+ void *p;
+
+ len = getpagesize();
+
+ expect_lookup(RELPATH, ino, bufsize);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READ &&
+ in.header.nodeid == ino &&
+ in.body.read.fh == Read::FH);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnErrno(EIO)));
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
+ ASSERT_NE(MAP_FAILED, p) << strerror(errno);
+
+ /*
+ * Actually accessing the mapped page would SIGBUS, which would break
+ * googletest's TearDown. But mlock will return ENOMEM if the memory
+ * access would've SIGBUSsed
+ */
+ ASSERT_NE(0, mlock(p, bufsize));
+ ASSERT_EQ(errno, ENOMEM);
+ //ASSERT_NE(SIG_ERR, signal(SIGBUS, sigbus_handler)) << strerror(errno);
+ //ASSERT_NE(0, memcmp(p, CONTENTS, bufsize));
+ //ASSERT_EQ(1, ReadSigbus::s_signaled);
+
+ ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
+ leak(fd);
+}
+
/*
* A read via mmap comes up short, indicating that the file was truncated
* server-side.
@@ -634,6 +680,77 @@
leak(fd);
}
+/*
+ * During VOP_GETPAGES, the FUSE server fails a FUSE_GETATTR operation. This
+ * almost certainly indicates a buggy FUSE server, and our goal should be not
+ * to panic. Instead, generate SIGBUS.
+ */
+TEST_F(Read, mmap_getblksz_fail)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ Sequence seq;
+ uint64_t ino = 42;
+ int fd;
+ ssize_t len;
+ size_t bufsize = strlen(CONTENTS);
+ mode_t mode = S_IFREG | 0644;
+ void *p;
+
+ len = getpagesize();
+
+ FuseTest::expect_lookup(RELPATH, ino, mode, bufsize, 1, 0);
+ /* Expect two GETATTR calls that succeed, followed by one that fail. */
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).Times(2)
+ .InSequence(seq)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino;
+ out.body.attr.attr.mode = mode;
+ out.body.attr.attr.size = bufsize;
+ out.body.attr.attr_valid = 0;
+ })));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).InSequence(seq)
+ .WillRepeatedly(Invoke(ReturnErrno(EIO)));
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READ);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
+ ASSERT_NE(MAP_FAILED, p) << strerror(errno);
+
+ /*
+ * Actually accessing the mapped page would SIGBUS, which would break
+ * googletest's TearDown. But mlock will return ENOMEM if the memory
+ * access would've SIGBUSsed
+ */
+ ASSERT_NE(0, mlock(p, bufsize));
+ ASSERT_EQ(errno, ENOMEM);
+
+ ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
+ leak(fd);
+}
+
/*
* Just as when FOPEN_DIRECT_IO is used, reads with O_DIRECT should bypass
* cache and to straight to the daemon

File Metadata

Mime Type
text/plain
Expires
Wed, Aug 12, 10:58 PM (7 h, 12 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
36583716
Default Alt Text
D31994.id95266.diff (4 KB)

Event Timeline