|
|
1d442b |
From: Stefan Hajnoczi <stefanha@redhat.com>
|
|
|
1d442b |
Date: Mon, 27 Jan 2020 19:01:35 +0000
|
|
|
1d442b |
Subject: [PATCH] virtiofsd: fix libfuse information leaks
|
|
|
1d442b |
MIME-Version: 1.0
|
|
|
1d442b |
Content-Type: text/plain; charset=UTF-8
|
|
|
1d442b |
Content-Transfer-Encoding: 8bit
|
|
|
1d442b |
|
|
|
1d442b |
Some FUSE message replies contain padding fields that are not
|
|
|
1d442b |
initialized by libfuse. This is fine in traditional FUSE applications
|
|
|
1d442b |
because the kernel is trusted. virtiofsd does not trust the guest and
|
|
|
1d442b |
must not expose uninitialized memory.
|
|
|
1d442b |
|
|
|
1d442b |
Use C struct initializers to automatically zero out memory. Not all of
|
|
|
1d442b |
these code changes are strictly necessary but they will prevent future
|
|
|
1d442b |
information leaks if the structs are extended.
|
|
|
1d442b |
|
|
|
1d442b |
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
|
|
1d442b |
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
|
|
|
1d442b |
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
|
|
|
1d442b |
Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
|
|
|
1d442b |
(cherry picked from commit 3db2876a0153ac7103c077c53090e020faffb3ea)
|
|
|
1d442b |
---
|
|
|
1d442b |
tools/virtiofsd/fuse_lowlevel.c | 150 ++++++++++++++++----------------
|
|
|
1d442b |
1 file changed, 76 insertions(+), 74 deletions(-)
|
|
|
1d442b |
|
|
|
1d442b |
diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c
|
|
|
1d442b |
index 2d6dc5a680..6ceb33d913 100644
|
|
|
1d442b |
--- a/tools/virtiofsd/fuse_lowlevel.c
|
|
|
1d442b |
+++ b/tools/virtiofsd/fuse_lowlevel.c
|
|
|
1d442b |
@@ -44,21 +44,23 @@ static __attribute__((constructor)) void fuse_ll_init_pagesize(void)
|
|
|
1d442b |
|
|
|
1d442b |
static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr)
|
|
|
1d442b |
{
|
|
|
1d442b |
- attr->ino = stbuf->st_ino;
|
|
|
1d442b |
- attr->mode = stbuf->st_mode;
|
|
|
1d442b |
- attr->nlink = stbuf->st_nlink;
|
|
|
1d442b |
- attr->uid = stbuf->st_uid;
|
|
|
1d442b |
- attr->gid = stbuf->st_gid;
|
|
|
1d442b |
- attr->rdev = stbuf->st_rdev;
|
|
|
1d442b |
- attr->size = stbuf->st_size;
|
|
|
1d442b |
- attr->blksize = stbuf->st_blksize;
|
|
|
1d442b |
- attr->blocks = stbuf->st_blocks;
|
|
|
1d442b |
- attr->atime = stbuf->st_atime;
|
|
|
1d442b |
- attr->mtime = stbuf->st_mtime;
|
|
|
1d442b |
- attr->ctime = stbuf->st_ctime;
|
|
|
1d442b |
- attr->atimensec = ST_ATIM_NSEC(stbuf);
|
|
|
1d442b |
- attr->mtimensec = ST_MTIM_NSEC(stbuf);
|
|
|
1d442b |
- attr->ctimensec = ST_CTIM_NSEC(stbuf);
|
|
|
1d442b |
+ *attr = (struct fuse_attr){
|
|
|
1d442b |
+ .ino = stbuf->st_ino,
|
|
|
1d442b |
+ .mode = stbuf->st_mode,
|
|
|
1d442b |
+ .nlink = stbuf->st_nlink,
|
|
|
1d442b |
+ .uid = stbuf->st_uid,
|
|
|
1d442b |
+ .gid = stbuf->st_gid,
|
|
|
1d442b |
+ .rdev = stbuf->st_rdev,
|
|
|
1d442b |
+ .size = stbuf->st_size,
|
|
|
1d442b |
+ .blksize = stbuf->st_blksize,
|
|
|
1d442b |
+ .blocks = stbuf->st_blocks,
|
|
|
1d442b |
+ .atime = stbuf->st_atime,
|
|
|
1d442b |
+ .mtime = stbuf->st_mtime,
|
|
|
1d442b |
+ .ctime = stbuf->st_ctime,
|
|
|
1d442b |
+ .atimensec = ST_ATIM_NSEC(stbuf),
|
|
|
1d442b |
+ .mtimensec = ST_MTIM_NSEC(stbuf),
|
|
|
1d442b |
+ .ctimensec = ST_CTIM_NSEC(stbuf),
|
|
|
1d442b |
+ };
|
|
|
1d442b |
}
|
|
|
1d442b |
|
|
|
1d442b |
static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf)
|
|
|
1d442b |
@@ -183,16 +185,16 @@ static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch,
|
|
|
1d442b |
int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
|
|
|
1d442b |
int count)
|
|
|
1d442b |
{
|
|
|
1d442b |
- struct fuse_out_header out;
|
|
|
1d442b |
+ struct fuse_out_header out = {
|
|
|
1d442b |
+ .unique = req->unique,
|
|
|
1d442b |
+ .error = error,
|
|
|
1d442b |
+ };
|
|
|
1d442b |
|
|
|
1d442b |
if (error <= -1000 || error > 0) {
|
|
|
1d442b |
fuse_log(FUSE_LOG_ERR, "fuse: bad error value: %i\n", error);
|
|
|
1d442b |
error = -ERANGE;
|
|
|
1d442b |
}
|
|
|
1d442b |
|
|
|
1d442b |
- out.unique = req->unique;
|
|
|
1d442b |
- out.error = error;
|
|
|
1d442b |
-
|
|
|
1d442b |
iov[0].iov_base = &out;
|
|
|
1d442b |
iov[0].iov_len = sizeof(struct fuse_out_header);
|
|
|
1d442b |
|
|
|
1d442b |
@@ -277,14 +279,16 @@ size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
|
|
|
1d442b |
static void convert_statfs(const struct statvfs *stbuf,
|
|
|
1d442b |
struct fuse_kstatfs *kstatfs)
|
|
|
1d442b |
{
|
|
|
1d442b |
- kstatfs->bsize = stbuf->f_bsize;
|
|
|
1d442b |
- kstatfs->frsize = stbuf->f_frsize;
|
|
|
1d442b |
- kstatfs->blocks = stbuf->f_blocks;
|
|
|
1d442b |
- kstatfs->bfree = stbuf->f_bfree;
|
|
|
1d442b |
- kstatfs->bavail = stbuf->f_bavail;
|
|
|
1d442b |
- kstatfs->files = stbuf->f_files;
|
|
|
1d442b |
- kstatfs->ffree = stbuf->f_ffree;
|
|
|
1d442b |
- kstatfs->namelen = stbuf->f_namemax;
|
|
|
1d442b |
+ *kstatfs = (struct fuse_kstatfs){
|
|
|
1d442b |
+ .bsize = stbuf->f_bsize,
|
|
|
1d442b |
+ .frsize = stbuf->f_frsize,
|
|
|
1d442b |
+ .blocks = stbuf->f_blocks,
|
|
|
1d442b |
+ .bfree = stbuf->f_bfree,
|
|
|
1d442b |
+ .bavail = stbuf->f_bavail,
|
|
|
1d442b |
+ .files = stbuf->f_files,
|
|
|
1d442b |
+ .ffree = stbuf->f_ffree,
|
|
|
1d442b |
+ .namelen = stbuf->f_namemax,
|
|
|
1d442b |
+ };
|
|
|
1d442b |
}
|
|
|
1d442b |
|
|
|
1d442b |
static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize)
|
|
|
1d442b |
@@ -328,12 +332,14 @@ static unsigned int calc_timeout_nsec(double t)
|
|
|
1d442b |
static void fill_entry(struct fuse_entry_out *arg,
|
|
|
1d442b |
const struct fuse_entry_param *e)
|
|
|
1d442b |
{
|
|
|
1d442b |
- arg->nodeid = e->ino;
|
|
|
1d442b |
- arg->generation = e->generation;
|
|
|
1d442b |
- arg->entry_valid = calc_timeout_sec(e->entry_timeout);
|
|
|
1d442b |
- arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout);
|
|
|
1d442b |
- arg->attr_valid = calc_timeout_sec(e->attr_timeout);
|
|
|
1d442b |
- arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout);
|
|
|
1d442b |
+ *arg = (struct fuse_entry_out){
|
|
|
1d442b |
+ .nodeid = e->ino,
|
|
|
1d442b |
+ .generation = e->generation,
|
|
|
1d442b |
+ .entry_valid = calc_timeout_sec(e->entry_timeout),
|
|
|
1d442b |
+ .entry_valid_nsec = calc_timeout_nsec(e->entry_timeout),
|
|
|
1d442b |
+ .attr_valid = calc_timeout_sec(e->attr_timeout),
|
|
|
1d442b |
+ .attr_valid_nsec = calc_timeout_nsec(e->attr_timeout),
|
|
|
1d442b |
+ };
|
|
|
1d442b |
convert_stat(&e->attr, &arg->attr);
|
|
|
1d442b |
}
|
|
|
1d442b |
|
|
|
1d442b |
@@ -362,10 +368,12 @@ size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize,
|
|
|
1d442b |
fill_entry(&dp->entry_out, e);
|
|
|
1d442b |
|
|
|
1d442b |
struct fuse_dirent *dirent = &dp->dirent;
|
|
|
1d442b |
- dirent->ino = e->attr.st_ino;
|
|
|
1d442b |
- dirent->off = off;
|
|
|
1d442b |
- dirent->namelen = namelen;
|
|
|
1d442b |
- dirent->type = (e->attr.st_mode & S_IFMT) >> 12;
|
|
|
1d442b |
+ *dirent = (struct fuse_dirent){
|
|
|
1d442b |
+ .ino = e->attr.st_ino,
|
|
|
1d442b |
+ .off = off,
|
|
|
1d442b |
+ .namelen = namelen,
|
|
|
1d442b |
+ .type = (e->attr.st_mode & S_IFMT) >> 12,
|
|
|
1d442b |
+ };
|
|
|
1d442b |
memcpy(dirent->name, name, namelen);
|
|
|
1d442b |
memset(dirent->name + namelen, 0, entlen_padded - entlen);
|
|
|
1d442b |
|
|
|
1d442b |
@@ -496,15 +504,14 @@ static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
|
|
|
1d442b |
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv)
|
|
|
1d442b |
{
|
|
|
1d442b |
struct iovec iov[2];
|
|
|
1d442b |
- struct fuse_out_header out;
|
|
|
1d442b |
+ struct fuse_out_header out = {
|
|
|
1d442b |
+ .unique = req->unique,
|
|
|
1d442b |
+ };
|
|
|
1d442b |
int res;
|
|
|
1d442b |
|
|
|
1d442b |
iov[0].iov_base = &out;
|
|
|
1d442b |
iov[0].iov_len = sizeof(struct fuse_out_header);
|
|
|
1d442b |
|
|
|
1d442b |
- out.unique = req->unique;
|
|
|
1d442b |
- out.error = 0;
|
|
|
1d442b |
-
|
|
|
1d442b |
res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv);
|
|
|
1d442b |
if (res <= 0) {
|
|
|
1d442b |
fuse_free_req(req);
|
|
|
1d442b |
@@ -2145,14 +2152,14 @@ static void do_destroy(fuse_req_t req, fuse_ino_t nodeid,
|
|
|
1d442b |
static int send_notify_iov(struct fuse_session *se, int notify_code,
|
|
|
1d442b |
struct iovec *iov, int count)
|
|
|
1d442b |
{
|
|
|
1d442b |
- struct fuse_out_header out;
|
|
|
1d442b |
+ struct fuse_out_header out = {
|
|
|
1d442b |
+ .error = notify_code,
|
|
|
1d442b |
+ };
|
|
|
1d442b |
|
|
|
1d442b |
if (!se->got_init) {
|
|
|
1d442b |
return -ENOTCONN;
|
|
|
1d442b |
}
|
|
|
1d442b |
|
|
|
1d442b |
- out.unique = 0;
|
|
|
1d442b |
- out.error = notify_code;
|
|
|
1d442b |
iov[0].iov_base = &out;
|
|
|
1d442b |
iov[0].iov_len = sizeof(struct fuse_out_header);
|
|
|
1d442b |
|
|
|
1d442b |
@@ -2162,11 +2169,11 @@ static int send_notify_iov(struct fuse_session *se, int notify_code,
|
|
|
1d442b |
int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
|
|
|
1d442b |
{
|
|
|
1d442b |
if (ph != NULL) {
|
|
|
1d442b |
- struct fuse_notify_poll_wakeup_out outarg;
|
|
|
1d442b |
+ struct fuse_notify_poll_wakeup_out outarg = {
|
|
|
1d442b |
+ .kh = ph->kh,
|
|
|
1d442b |
+ };
|
|
|
1d442b |
struct iovec iov[2];
|
|
|
1d442b |
|
|
|
1d442b |
- outarg.kh = ph->kh;
|
|
|
1d442b |
-
|
|
|
1d442b |
iov[1].iov_base = &outarg;
|
|
|
1d442b |
iov[1].iov_len = sizeof(outarg);
|
|
|
1d442b |
|
|
|
1d442b |
@@ -2179,17 +2186,17 @@ int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
|
|
|
1d442b |
int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino,
|
|
|
1d442b |
off_t off, off_t len)
|
|
|
1d442b |
{
|
|
|
1d442b |
- struct fuse_notify_inval_inode_out outarg;
|
|
|
1d442b |
+ struct fuse_notify_inval_inode_out outarg = {
|
|
|
1d442b |
+ .ino = ino,
|
|
|
1d442b |
+ .off = off,
|
|
|
1d442b |
+ .len = len,
|
|
|
1d442b |
+ };
|
|
|
1d442b |
struct iovec iov[2];
|
|
|
1d442b |
|
|
|
1d442b |
if (!se) {
|
|
|
1d442b |
return -EINVAL;
|
|
|
1d442b |
}
|
|
|
1d442b |
|
|
|
1d442b |
- outarg.ino = ino;
|
|
|
1d442b |
- outarg.off = off;
|
|
|
1d442b |
- outarg.len = len;
|
|
|
1d442b |
-
|
|
|
1d442b |
iov[1].iov_base = &outarg;
|
|
|
1d442b |
iov[1].iov_len = sizeof(outarg);
|
|
|
1d442b |
|
|
|
1d442b |
@@ -2199,17 +2206,16 @@ int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino,
|
|
|
1d442b |
int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
|
|
|
1d442b |
const char *name, size_t namelen)
|
|
|
1d442b |
{
|
|
|
1d442b |
- struct fuse_notify_inval_entry_out outarg;
|
|
|
1d442b |
+ struct fuse_notify_inval_entry_out outarg = {
|
|
|
1d442b |
+ .parent = parent,
|
|
|
1d442b |
+ .namelen = namelen,
|
|
|
1d442b |
+ };
|
|
|
1d442b |
struct iovec iov[3];
|
|
|
1d442b |
|
|
|
1d442b |
if (!se) {
|
|
|
1d442b |
return -EINVAL;
|
|
|
1d442b |
}
|
|
|
1d442b |
|
|
|
1d442b |
- outarg.parent = parent;
|
|
|
1d442b |
- outarg.namelen = namelen;
|
|
|
1d442b |
- outarg.padding = 0;
|
|
|
1d442b |
-
|
|
|
1d442b |
iov[1].iov_base = &outarg;
|
|
|
1d442b |
iov[1].iov_len = sizeof(outarg);
|
|
|
1d442b |
iov[2].iov_base = (void *)name;
|
|
|
1d442b |
@@ -2222,18 +2228,17 @@ int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent,
|
|
|
1d442b |
fuse_ino_t child, const char *name,
|
|
|
1d442b |
size_t namelen)
|
|
|
1d442b |
{
|
|
|
1d442b |
- struct fuse_notify_delete_out outarg;
|
|
|
1d442b |
+ struct fuse_notify_delete_out outarg = {
|
|
|
1d442b |
+ .parent = parent,
|
|
|
1d442b |
+ .child = child,
|
|
|
1d442b |
+ .namelen = namelen,
|
|
|
1d442b |
+ };
|
|
|
1d442b |
struct iovec iov[3];
|
|
|
1d442b |
|
|
|
1d442b |
if (!se) {
|
|
|
1d442b |
return -EINVAL;
|
|
|
1d442b |
}
|
|
|
1d442b |
|
|
|
1d442b |
- outarg.parent = parent;
|
|
|
1d442b |
- outarg.child = child;
|
|
|
1d442b |
- outarg.namelen = namelen;
|
|
|
1d442b |
- outarg.padding = 0;
|
|
|
1d442b |
-
|
|
|
1d442b |
iov[1].iov_base = &outarg;
|
|
|
1d442b |
iov[1].iov_len = sizeof(outarg);
|
|
|
1d442b |
iov[2].iov_base = (void *)name;
|
|
|
1d442b |
@@ -2245,24 +2250,21 @@ int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent,
|
|
|
1d442b |
int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino,
|
|
|
1d442b |
off_t offset, struct fuse_bufvec *bufv)
|
|
|
1d442b |
{
|
|
|
1d442b |
- struct fuse_out_header out;
|
|
|
1d442b |
- struct fuse_notify_store_out outarg;
|
|
|
1d442b |
+ struct fuse_out_header out = {
|
|
|
1d442b |
+ .error = FUSE_NOTIFY_STORE,
|
|
|
1d442b |
+ };
|
|
|
1d442b |
+ struct fuse_notify_store_out outarg = {
|
|
|
1d442b |
+ .nodeid = ino,
|
|
|
1d442b |
+ .offset = offset,
|
|
|
1d442b |
+ .size = fuse_buf_size(bufv),
|
|
|
1d442b |
+ };
|
|
|
1d442b |
struct iovec iov[3];
|
|
|
1d442b |
- size_t size = fuse_buf_size(bufv);
|
|
|
1d442b |
int res;
|
|
|
1d442b |
|
|
|
1d442b |
if (!se) {
|
|
|
1d442b |
return -EINVAL;
|
|
|
1d442b |
}
|
|
|
1d442b |
|
|
|
1d442b |
- out.unique = 0;
|
|
|
1d442b |
- out.error = FUSE_NOTIFY_STORE;
|
|
|
1d442b |
-
|
|
|
1d442b |
- outarg.nodeid = ino;
|
|
|
1d442b |
- outarg.offset = offset;
|
|
|
1d442b |
- outarg.size = size;
|
|
|
1d442b |
- outarg.padding = 0;
|
|
|
1d442b |
-
|
|
|
1d442b |
iov[0].iov_base = &out;
|
|
|
1d442b |
iov[0].iov_len = sizeof(out);
|
|
|
1d442b |
iov[1].iov_base = &outarg;
|