Page MenuHomeFreeBSD

D42081.diff
No OneTemporary

D42081.diff

diff --git a/sys/fs/fuse/fuse_ipc.h b/sys/fs/fuse/fuse_ipc.h
--- a/sys/fs/fuse/fuse_ipc.h
+++ b/sys/fs/fuse/fuse_ipc.h
@@ -239,6 +239,7 @@
#define FSESS_WARN_CACHE_INCOHERENT 0x200000 /* Read cache incoherent */
#define FSESS_WARN_WB_CACHE_INCOHERENT 0x400000 /* WB cache incoherent */
#define FSESS_WARN_ILLEGAL_INODE 0x800000 /* Illegal inode for new file */
+#define FSESS_WARN_READLINK_EMBEDDED_NUL 0x1000000 /* corrupt READLINK output */
#define FSESS_MNTOPTS_MASK ( \
FSESS_DAEMON_CAN_SPY | FSESS_PUSH_SYMLINKS_IN | \
FSESS_DEFAULT_PERMISSIONS | FSESS_INTR)
diff --git a/sys/fs/fuse/fuse_vnops.c b/sys/fs/fuse/fuse_vnops.c
--- a/sys/fs/fuse/fuse_vnops.c
+++ b/sys/fs/fuse/fuse_vnops.c
@@ -2007,6 +2007,13 @@
if (err) {
goto out;
}
+ if (strnlen(fdi.answ, fdi.iosize) + 1 < fdi.iosize) {
+ struct fuse_data *data = fuse_get_mpdata(vnode_mount(vp));
+ fuse_warn(data, FSESS_WARN_READLINK_EMBEDDED_NUL,
+ "Returned an embedded NUL from FUSE_READLINK.");
+ err = EIO;
+ goto out;
+ }
if (((char *)fdi.answ)[0] == '/' &&
fuse_get_mpdata(vnode_mount(vp))->dataflags & FSESS_PUSH_SYMLINKS_IN) {
char *mpth = vnode_mount(vp)->mnt_stat.f_mntonname;
diff --git a/tests/sys/fs/fusefs/readlink.cc b/tests/sys/fs/fusefs/readlink.cc
--- a/tests/sys/fs/fusefs/readlink.cc
+++ b/tests/sys/fs/fusefs/readlink.cc
@@ -79,6 +79,45 @@
EXPECT_EQ(ELOOP, errno);
}
+/*
+ * If a malicious or buggy server returns a NUL in the FUSE_READLINK result, it
+ * should be handled gracefully.
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=274268
+ */
+TEST_F(Readlink, embedded_nul)
+{
+ const char FULLPATH[] = "mountpoint/src";
+ const char RELPATH[] = "src";
+ const char dst[] = "dst\0stuff";
+ char buf[80];
+ const uint64_t ino = 42;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFLNK | 0777;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READLINK &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ memcpy(out.body.str, dst, sizeof(dst));
+ out.header.len = sizeof(out.header) + sizeof(dst) + 1;
+ })));
+
+ EXPECT_EQ(-1, readlink(FULLPATH, buf, sizeof(buf)));
+ EXPECT_EQ(EIO, errno);
+ EXPECT_EQ(-1, access(FULLPATH, R_OK));
+ EXPECT_EQ(EIO, errno);
+}
+
TEST_F(Readlink, ok)
{
const char FULLPATH[] = "mountpoint/src";

File Metadata

Mime Type
text/plain
Expires
Thu, Sep 26, 12:24 AM (21 h, 44 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
12801477
Default Alt Text
D42081.diff (2 KB)

Event Timeline