fusefs: Fix iosize for FUSE_WRITE in 7.8 compat mode
When communicating with a FUSE server that implements version 7.8 (or older) of the FUSE protocol, the FUSE_WRITE request structure is 16 bytes shorter than normal. The protocol version check wasn't applied universally, leading to an extra 16 bytes being sent to such servers. The extra bytes were allocated and bzero()d, so there was no information disclosure. Reviewed by: emaste MFC after: 3 days MFC-With: r350665 Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D21557
This commit is contained in:
parent
6a82e3a0a3
commit
67e29a8912
@ -555,9 +555,17 @@ fuse_write_directbackend(struct vnode *vp, struct uio *uio,
|
||||
fdisp_init(&fdi, 0);
|
||||
|
||||
while (uio->uio_resid > 0) {
|
||||
size_t sizeof_fwi;
|
||||
|
||||
if (fuse_libabi_geq(data, 7, 9)) {
|
||||
sizeof_fwi = sizeof(*fwi);
|
||||
} else {
|
||||
sizeof_fwi = FUSE_COMPAT_WRITE_IN_SIZE;
|
||||
}
|
||||
|
||||
chunksize = MIN(uio->uio_resid, data->max_write);
|
||||
|
||||
fdi.iosize = sizeof(*fwi) + chunksize;
|
||||
fdi.iosize = sizeof_fwi + chunksize;
|
||||
fdisp_make_vp(&fdi, FUSE_WRITE, vp, uio->uio_td, cred);
|
||||
|
||||
fwi = fdi.indata;
|
||||
@ -567,11 +575,8 @@ fuse_write_directbackend(struct vnode *vp, struct uio *uio,
|
||||
fwi->write_flags = write_flags;
|
||||
if (fuse_libabi_geq(data, 7, 9)) {
|
||||
fwi->flags = fufh_type_2_fflags(fufh->fufh_type);
|
||||
fwi_data = (char *)fdi.indata + sizeof(*fwi);
|
||||
} else {
|
||||
fwi_data = (char *)fdi.indata +
|
||||
FUSE_COMPAT_WRITE_IN_SIZE;
|
||||
}
|
||||
fwi_data = (char *)fdi.indata + sizeof_fwi;
|
||||
|
||||
if ((err = uiomove(fwi_data, chunksize, uio)))
|
||||
break;
|
||||
@ -629,7 +634,7 @@ fuse_write_directbackend(struct vnode *vp, struct uio *uio,
|
||||
break;
|
||||
} else {
|
||||
/* Resend the unwritten portion of data */
|
||||
fdi.iosize = sizeof(*fwi) + diff;
|
||||
fdi.iosize = sizeof_fwi + diff;
|
||||
/* Refresh fdi without clearing data buffer */
|
||||
fdisp_refresh_vp(&fdi, FUSE_WRITE, vp,
|
||||
uio->uio_td, cred);
|
||||
|
@ -155,14 +155,15 @@ void sigint_handler(int __unused sig) {
|
||||
// Don't do anything except interrupt the daemon's read(2) call
|
||||
}
|
||||
|
||||
void MockFS::debug_request(const mockfs_buf_in &in)
|
||||
void MockFS::debug_request(const mockfs_buf_in &in, ssize_t buflen)
|
||||
{
|
||||
printf("%-11s ino=%2" PRIu64, opcode2opname(in.header.opcode),
|
||||
in.header.nodeid);
|
||||
if (verbosity > 1) {
|
||||
printf(" uid=%5u gid=%5u pid=%5u unique=%" PRIu64 " len=%u",
|
||||
printf(" uid=%5u gid=%5u pid=%5u unique=%" PRIu64 " len=%u"
|
||||
" buflen=%zd",
|
||||
in.header.uid, in.header.gid, in.header.pid,
|
||||
in.header.unique, in.header.len);
|
||||
in.header.unique, in.header.len, buflen);
|
||||
}
|
||||
switch (in.header.opcode) {
|
||||
const char *name, *value;
|
||||
@ -470,7 +471,7 @@ MockFS::~MockFS() {
|
||||
close(m_kq);
|
||||
}
|
||||
|
||||
void MockFS::audit_request(const mockfs_buf_in &in) {
|
||||
void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) {
|
||||
uint32_t inlen = in.header.len;
|
||||
size_t fih = sizeof(in.header);
|
||||
switch (in.header.opcode) {
|
||||
@ -478,19 +479,24 @@ void MockFS::audit_request(const mockfs_buf_in &in) {
|
||||
case FUSE_RMDIR:
|
||||
case FUSE_SYMLINK:
|
||||
case FUSE_UNLINK:
|
||||
ASSERT_GT(inlen, fih) << "Missing request filename";
|
||||
EXPECT_GT(inlen, fih) << "Missing request filename";
|
||||
// No redundant information for checking buflen
|
||||
break;
|
||||
case FUSE_FORGET:
|
||||
ASSERT_EQ(inlen, fih + sizeof(in.body.forget));
|
||||
EXPECT_EQ(inlen, fih + sizeof(in.body.forget));
|
||||
EXPECT_EQ((size_t)buflen, inlen);
|
||||
break;
|
||||
case FUSE_GETATTR:
|
||||
ASSERT_EQ(inlen, fih + sizeof(in.body.getattr));
|
||||
EXPECT_EQ(inlen, fih + sizeof(in.body.getattr));
|
||||
EXPECT_EQ((size_t)buflen, inlen);
|
||||
break;
|
||||
case FUSE_SETATTR:
|
||||
ASSERT_EQ(inlen, fih + sizeof(in.body.setattr));
|
||||
EXPECT_EQ(inlen, fih + sizeof(in.body.setattr));
|
||||
EXPECT_EQ((size_t)buflen, inlen);
|
||||
break;
|
||||
case FUSE_READLINK:
|
||||
ASSERT_EQ(inlen, fih) << "Unexpected request body";
|
||||
EXPECT_EQ(inlen, fih) << "Unexpected request body";
|
||||
EXPECT_EQ((size_t)buflen, inlen);
|
||||
break;
|
||||
case FUSE_MKNOD:
|
||||
{
|
||||
@ -499,33 +505,39 @@ void MockFS::audit_request(const mockfs_buf_in &in) {
|
||||
s = sizeof(in.body.mknod);
|
||||
else
|
||||
s = FUSE_COMPAT_MKNOD_IN_SIZE;
|
||||
ASSERT_GE(inlen, fih + s) << "Missing request body";
|
||||
ASSERT_GT(inlen, fih + s) << "Missing request filename";
|
||||
EXPECT_GE(inlen, fih + s) << "Missing request body";
|
||||
EXPECT_GT(inlen, fih + s) << "Missing request filename";
|
||||
// No redundant information for checking buflen
|
||||
break;
|
||||
}
|
||||
case FUSE_MKDIR:
|
||||
ASSERT_GE(inlen, fih + sizeof(in.body.mkdir)) <<
|
||||
EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) <<
|
||||
"Missing request body";
|
||||
ASSERT_GT(inlen, fih + sizeof(in.body.mkdir)) <<
|
||||
EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) <<
|
||||
"Missing request filename";
|
||||
// No redundant information for checking buflen
|
||||
break;
|
||||
case FUSE_RENAME:
|
||||
ASSERT_GE(inlen, fih + sizeof(in.body.rename)) <<
|
||||
EXPECT_GE(inlen, fih + sizeof(in.body.rename)) <<
|
||||
"Missing request body";
|
||||
ASSERT_GT(inlen, fih + sizeof(in.body.rename)) <<
|
||||
EXPECT_GT(inlen, fih + sizeof(in.body.rename)) <<
|
||||
"Missing request filename";
|
||||
// No redundant information for checking buflen
|
||||
break;
|
||||
case FUSE_LINK:
|
||||
ASSERT_GE(inlen, fih + sizeof(in.body.link)) <<
|
||||
EXPECT_GE(inlen, fih + sizeof(in.body.link)) <<
|
||||
"Missing request body";
|
||||
ASSERT_GT(inlen, fih + sizeof(in.body.link)) <<
|
||||
EXPECT_GT(inlen, fih + sizeof(in.body.link)) <<
|
||||
"Missing request filename";
|
||||
// No redundant information for checking buflen
|
||||
break;
|
||||
case FUSE_OPEN:
|
||||
ASSERT_EQ(inlen, fih + sizeof(in.body.open));
|
||||
EXPECT_EQ(inlen, fih + sizeof(in.body.open));
|
||||
EXPECT_EQ((size_t)buflen, inlen);
|
||||
break;
|
||||
case FUSE_READ:
|
||||
ASSERT_EQ(inlen, fih + sizeof(in.body.read));
|
||||
EXPECT_EQ(inlen, fih + sizeof(in.body.read));
|
||||
EXPECT_EQ((size_t)buflen, inlen);
|
||||
break;
|
||||
case FUSE_WRITE:
|
||||
{
|
||||
@ -535,75 +547,94 @@ void MockFS::audit_request(const mockfs_buf_in &in) {
|
||||
s = sizeof(in.body.write);
|
||||
else
|
||||
s = FUSE_COMPAT_WRITE_IN_SIZE;
|
||||
ASSERT_GE(inlen, fih + s) << "Missing request body";
|
||||
// I suppose a 0-byte write should be allowed
|
||||
EXPECT_GE(inlen, fih + s) << "Missing request body";
|
||||
EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size);
|
||||
break;
|
||||
}
|
||||
case FUSE_DESTROY:
|
||||
case FUSE_STATFS:
|
||||
ASSERT_EQ(inlen, fih);
|
||||
EXPECT_EQ(inlen, fih);
|
||||
EXPECT_EQ((size_t)buflen, inlen);
|
||||
break;
|
||||
case FUSE_RELEASE:
|
||||
ASSERT_EQ(inlen, fih + sizeof(in.body.release));
|
||||
EXPECT_EQ(inlen, fih + sizeof(in.body.release));
|
||||
EXPECT_EQ((size_t)buflen, inlen);
|
||||
break;
|
||||
case FUSE_FSYNC:
|
||||
case FUSE_FSYNCDIR:
|
||||
ASSERT_EQ(inlen, fih + sizeof(in.body.fsync));
|
||||
EXPECT_EQ(inlen, fih + sizeof(in.body.fsync));
|
||||
EXPECT_EQ((size_t)buflen, inlen);
|
||||
break;
|
||||
case FUSE_SETXATTR:
|
||||
ASSERT_GE(inlen, fih + sizeof(in.body.setxattr)) <<
|
||||
EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) <<
|
||||
"Missing request body";
|
||||
ASSERT_GT(inlen, fih + sizeof(in.body.setxattr)) <<
|
||||
EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) <<
|
||||
"Missing request attribute name";
|
||||
// No redundant information for checking buflen
|
||||
break;
|
||||
case FUSE_GETXATTR:
|
||||
ASSERT_GE(inlen, fih + sizeof(in.body.getxattr)) <<
|
||||
EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) <<
|
||||
"Missing request body";
|
||||
ASSERT_GT(inlen, fih + sizeof(in.body.getxattr)) <<
|
||||
EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) <<
|
||||
"Missing request attribute name";
|
||||
// No redundant information for checking buflen
|
||||
break;
|
||||
case FUSE_LISTXATTR:
|
||||
ASSERT_EQ(inlen, fih + sizeof(in.body.listxattr));
|
||||
EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr));
|
||||
EXPECT_EQ((size_t)buflen, inlen);
|
||||
break;
|
||||
case FUSE_REMOVEXATTR:
|
||||
ASSERT_GT(inlen, fih) << "Missing request attribute name";
|
||||
EXPECT_GT(inlen, fih) << "Missing request attribute name";
|
||||
// No redundant information for checking buflen
|
||||
break;
|
||||
case FUSE_FLUSH:
|
||||
ASSERT_EQ(inlen, fih + sizeof(in.body.flush));
|
||||
EXPECT_EQ(inlen, fih + sizeof(in.body.flush));
|
||||
EXPECT_EQ((size_t)buflen, inlen);
|
||||
break;
|
||||
case FUSE_INIT:
|
||||
ASSERT_EQ(inlen, fih + sizeof(in.body.init));
|
||||
EXPECT_EQ(inlen, fih + sizeof(in.body.init));
|
||||
EXPECT_EQ((size_t)buflen, inlen);
|
||||
break;
|
||||
case FUSE_OPENDIR:
|
||||
ASSERT_EQ(inlen, fih + sizeof(in.body.opendir));
|
||||
EXPECT_EQ(inlen, fih + sizeof(in.body.opendir));
|
||||
EXPECT_EQ((size_t)buflen, inlen);
|
||||
break;
|
||||
case FUSE_READDIR:
|
||||
ASSERT_EQ(inlen, fih + sizeof(in.body.readdir));
|
||||
EXPECT_EQ(inlen, fih + sizeof(in.body.readdir));
|
||||
EXPECT_EQ((size_t)buflen, inlen);
|
||||
break;
|
||||
case FUSE_RELEASEDIR:
|
||||
ASSERT_EQ(inlen, fih + sizeof(in.body.releasedir));
|
||||
EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir));
|
||||
EXPECT_EQ((size_t)buflen, inlen);
|
||||
break;
|
||||
case FUSE_GETLK:
|
||||
ASSERT_EQ(inlen, fih + sizeof(in.body.getlk));
|
||||
EXPECT_EQ(inlen, fih + sizeof(in.body.getlk));
|
||||
EXPECT_EQ((size_t)buflen, inlen);
|
||||
break;
|
||||
case FUSE_SETLK:
|
||||
case FUSE_SETLKW:
|
||||
ASSERT_EQ(inlen, fih + sizeof(in.body.setlk));
|
||||
EXPECT_EQ(inlen, fih + sizeof(in.body.setlk));
|
||||
EXPECT_EQ((size_t)buflen, inlen);
|
||||
break;
|
||||
case FUSE_ACCESS:
|
||||
ASSERT_EQ(inlen, fih + sizeof(in.body.access));
|
||||
EXPECT_EQ(inlen, fih + sizeof(in.body.access));
|
||||
EXPECT_EQ((size_t)buflen, inlen);
|
||||
break;
|
||||
case FUSE_CREATE:
|
||||
ASSERT_GE(inlen, fih + sizeof(in.body.create)) <<
|
||||
EXPECT_GE(inlen, fih + sizeof(in.body.create)) <<
|
||||
"Missing request body";
|
||||
ASSERT_GT(inlen, fih + sizeof(in.body.create)) <<
|
||||
EXPECT_GT(inlen, fih + sizeof(in.body.create)) <<
|
||||
"Missing request filename";
|
||||
// No redundant information for checking buflen
|
||||
break;
|
||||
case FUSE_INTERRUPT:
|
||||
ASSERT_EQ(inlen, fih + sizeof(in.body.interrupt));
|
||||
EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt));
|
||||
EXPECT_EQ((size_t)buflen, inlen);
|
||||
break;
|
||||
case FUSE_BMAP:
|
||||
ASSERT_EQ(inlen, fih + sizeof(in.body.bmap));
|
||||
EXPECT_EQ(inlen, fih + sizeof(in.body.bmap));
|
||||
EXPECT_EQ((size_t)buflen, inlen);
|
||||
break;
|
||||
case FUSE_NOTIFY_REPLY:
|
||||
case FUSE_BATCH_FORGET:
|
||||
@ -618,10 +649,13 @@ void MockFS::audit_request(const mockfs_buf_in &in) {
|
||||
}
|
||||
|
||||
void MockFS::init(uint32_t flags) {
|
||||
ssize_t buflen;
|
||||
|
||||
std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
|
||||
std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
|
||||
|
||||
read_request(*in);
|
||||
read_request(*in, buflen);
|
||||
audit_request(*in, buflen);
|
||||
ASSERT_EQ(FUSE_INIT, in->header.opcode);
|
||||
|
||||
out->header.unique = in->header.unique;
|
||||
@ -659,13 +693,15 @@ void MockFS::loop() {
|
||||
std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
|
||||
ASSERT_TRUE(in != NULL);
|
||||
while (!m_quit) {
|
||||
ssize_t buflen;
|
||||
|
||||
bzero(in.get(), sizeof(*in));
|
||||
read_request(*in);
|
||||
read_request(*in, buflen);
|
||||
if (m_quit)
|
||||
break;
|
||||
if (verbosity > 0)
|
||||
debug_request(*in);
|
||||
audit_request(*in);
|
||||
debug_request(*in, buflen);
|
||||
audit_request(*in, buflen);
|
||||
if (pid_ok((pid_t)in->header.pid)) {
|
||||
process(*in, out);
|
||||
} else {
|
||||
@ -766,8 +802,7 @@ void MockFS::process_default(const mockfs_buf_in& in,
|
||||
out.push_back(std::move(out0));
|
||||
}
|
||||
|
||||
void MockFS::read_request(mockfs_buf_in &in) {
|
||||
ssize_t res;
|
||||
void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) {
|
||||
int nready = 0;
|
||||
fd_set readfds;
|
||||
pollfd fds[1];
|
||||
|
@ -283,8 +283,8 @@ class MockFS {
|
||||
/* Timestamp granularity in nanoseconds */
|
||||
unsigned m_time_gran;
|
||||
|
||||
void audit_request(const mockfs_buf_in &in);
|
||||
void debug_request(const mockfs_buf_in&);
|
||||
void audit_request(const mockfs_buf_in &in, ssize_t buflen);
|
||||
void debug_request(const mockfs_buf_in&, ssize_t buflen);
|
||||
void debug_response(const mockfs_buf_out&);
|
||||
|
||||
/* Initialize a session after mounting */
|
||||
@ -300,8 +300,14 @@ class MockFS {
|
||||
/* Entry point for the daemon thread */
|
||||
static void* service(void*);
|
||||
|
||||
/* Read, but do not process, a single request from the kernel */
|
||||
void read_request(mockfs_buf_in& in);
|
||||
/*
|
||||
* Read, but do not process, a single request from the kernel
|
||||
*
|
||||
* @param in Return storage for the FUSE request
|
||||
* @param res Return value of read(2). If positive, the amount of
|
||||
* data read from the fuse device.
|
||||
*/
|
||||
void read_request(mockfs_buf_in& in, ssize_t& res);
|
||||
|
||||
/* Write a single response back to the kernel */
|
||||
void write_response(const mockfs_buf_out &out);
|
||||
|
Loading…
Reference in New Issue
Block a user