fusefs: fix an uninitialized memory access in fuse_vnop_deallocate

If the FUSE_GETATTR issued to query a file's size during
fuse_vnop_deallocate failed for any reason, then fuse_vnop_deallocate
would attempt to destroy an uninitialized fuse_dispatcher struct, with a
crash the likely result.  This bug only affects FUSE file systems that
implement FUSE_FALLOCATE, and is unlikely to be seen on those that don't
disable attribute caching.

Reported by:	Coverity Scan
CID:		1505308

(cherry picked from commit f93a50d69df2e996ff1d4f793d0dcb9de655ebdc)
This commit is contained in:
Alan Somers 2024-09-08 15:50:40 -06:00
parent f37dc94abc
commit cf2ed0edae
2 changed files with 52 additions and 1 deletions

View file

@ -3082,8 +3082,8 @@ fuse_vnop_deallocate(struct vop_deallocate_args *ap)
false);
}
out:
fdisp_destroy(&fdi);
out:
if (closefufh)
fuse_filehandle_close(vp, fufh, curthread, cred);

View file

@ -310,6 +310,57 @@ TEST_F(Fspacectl, erofs)
leak(fd);
}
/*
* If FUSE_GETATTR fails when determining the size of the file, fspacectl
* should fail gracefully. This failure mode is easiest to trigger when
* attribute caching is disabled.
*/
TEST_F(Fspacectl, getattr_fails)
{
const char FULLPATH[] = "mountpoint/some_file.txt";
const char RELPATH[] = "some_file.txt";
Sequence seq;
struct spacectl_range rqsr;
const uint64_t ino = 42;
const uint64_t fsize = 2000;
int fd;
expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1, 0);
expect_open(ino, 0, 1);
EXPECT_CALL(*m_mock, process(
ResultOf([](auto in) {
return (in.header.opcode == FUSE_GETATTR &&
in.header.nodeid == ino);
}, Eq(true)),
_)
).Times(1)
.InSequence(seq)
.WillOnce(Invoke(ReturnImmediate([](auto i __unused, auto& out) {
SET_OUT_HEADER_LEN(out, attr);
out.body.attr.attr.ino = ino;
out.body.attr.attr.mode = S_IFREG | 0644;
out.body.attr.attr.size = fsize;
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)
.WillOnce(ReturnErrno(EIO));
fd = open(FULLPATH, O_RDWR);
ASSERT_LE(0, fd) << strerror(errno);
rqsr.r_offset = 500;
rqsr.r_len = 1000;
EXPECT_EQ(-1, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
EXPECT_EQ(EIO, errno);
leak(fd);
}
TEST_F(Fspacectl, ok)
{
const char FULLPATH[] = "mountpoint/some_file.txt";