Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F144291313
D31994.id95327.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
6 KB
Referenced Files
None
Subscribers
None
D31994.id95327.diff
View Options
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
@@ -40,6 +40,8 @@
#include <aio.h>
#include <fcntl.h>
#include <semaphore.h>
+#include <setjmp.h>
+#include <signal.h>
#include <unistd.h>
}
@@ -103,6 +105,33 @@
}
};
+class ReadSigbus: public Read
+{
+public:
+static jmp_buf s_jmpbuf;
+static sig_atomic_t s_si_addr;
+
+void TearDown() {
+ struct sigaction sa;
+
+ bzero(&sa, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGBUS, &sa, NULL);
+
+ FuseTest::TearDown();
+}
+
+};
+
+static void
+handle_sigbus(int signo __unused, siginfo_t *info, void *uap __unused) {
+ ReadSigbus::s_si_addr = (sig_atomic_t)info->si_addr;
+ longjmp(ReadSigbus::s_jmpbuf, 1);
+}
+
+jmp_buf ReadSigbus::s_jmpbuf;
+sig_atomic_t ReadSigbus::s_si_addr;
+
/* AIO reads need to set the header's pid field correctly */
/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
TEST_F(AioRead, aio_read)
@@ -584,6 +613,55 @@
leak(fd);
}
+/* Read of an mmap()ed file fails */
+TEST_F(ReadSigbus, mmap_eio)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ struct sigaction sa;
+ 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);
+
+ /* Accessing the mapped page should return SIGBUS. */
+
+ bzero(&sa, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+ sa.sa_sigaction = handle_sigbus;
+ sa.sa_flags = SA_RESETHAND | SA_SIGINFO;
+ ASSERT_EQ(0, sigaction(SIGBUS, &sa, NULL)) << strerror(errno);
+ if (setjmp(ReadSigbus::s_jmpbuf) == 0) {
+ volatile char x __unused = *(volatile char*)p;
+ FAIL() << "shouldn't get here";
+ }
+
+ ASSERT_EQ(p, (void*)ReadSigbus::s_si_addr);
+ 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 +712,82 @@
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(ReadSigbus, mmap_getblksz_fail)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ struct sigaction sa;
+ 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);
+
+ /* Accessing the mapped page should return SIGBUS. */
+ bzero(&sa, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+ sa.sa_sigaction = handle_sigbus;
+ sa.sa_flags = SA_RESETHAND | SA_SIGINFO;
+ ASSERT_EQ(0, sigaction(SIGBUS, &sa, NULL)) << strerror(errno);
+ if (setjmp(ReadSigbus::s_jmpbuf) == 0) {
+ volatile char x __unused = *(volatile char*)p;
+ FAIL() << "shouldn't get here";
+ }
+
+ ASSERT_EQ(p, (void*)ReadSigbus::s_si_addr);
+ 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
Details
Attached
Mime Type
text/plain
Expires
Sun, Feb 8, 4:31 PM (9 h, 26 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28473626
Default Alt Text
D31994.id95327.diff (6 KB)
Attached To
Mode
D31994: fusefs: don't panic if FUSE_GETATTR fails durint VOP_GETPAGES
Attached
Detach File
Event Timeline
Log In to Comment