mirror of
https://github.com/opnsense/src.git
synced 2026-05-28 04:12:45 -04:00
fusefs: sanitize FUSE_READLINK results for embedded NULs
If VOP_READLINK returns a path that contains a NUL, it will trigger an assertion in vfs_lookup. Sanitize such paths in fusefs, rejecting any and warning the user about the misbehaving server. PR: 274268 Sponsored by: Axcient Reviewed by: mjg, markj Differential Revision: https://reviews.freebsd.org/D42081 (cherry picked from commit 662ec2f781521c36b76af748d74bb0a3c2e27a76)
This commit is contained in:
parent
29de7af6ee
commit
8fca98f688
3 changed files with 47 additions and 0 deletions
|
|
@ -239,6 +239,7 @@ struct fuse_data {
|
|||
#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)
|
||||
|
|
|
|||
|
|
@ -2007,6 +2007,13 @@ fuse_vnop_readlink(struct vop_readlink_args *ap)
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -79,6 +79,45 @@ TEST_F(Readlink, eloop)
|
|||
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";
|
||||
|
|
|
|||
Loading…
Reference in a new issue