diff options
author | Niels de Vos <ndevos@redhat.com> | 2014-04-24 13:38:31 +0200 |
---|---|---|
committer | Niels de Vos <ndevos@redhat.com> | 2014-05-08 10:58:50 -0700 |
commit | 94282fcae8ecb0d53399baa4e96f3aaa443979f0 (patch) | |
tree | b6e072e9e26802ee7139fbf3940e71ae5f2b95d2 /xlators | |
parent | f46a3198b1a3d794c23b8ac2ab9334326c4918c8 (diff) |
fuse: prevent READDIR(P) from writing to much data to /dev/fuse
In an environment with mixed architectures (32-bit servers, 64-bit
client), it is possible that the on-wire Reply on a READDIR(P) procedure
contains more direntries than the client can fit in the maximum size
that the fuse-request indicated.
A direntry is a dynamically sized structure, because the structure
contains the name of the entry. The client sends a maximum size in the
READDIR(P) Call to the server, and the server uses this size to limit
the number of direntries to return. In case the server can pack more
direntries in the requested maximum size (due to alignment differences
between the architectures), it can happen that the client unpacks the
list of direntries into a buffer that exceeds the maximum size that was
given in the initial fuse-request.
This change introduces a check for the maximum requested size of the
fuse-response in fuse_readdir_cbk() and fuse_readdirp_cbk(). When the
conversion from gluster-direntries to the fuse-direntry format takes
place, the maximum size is checked, and the 'extra' direntries are
discarded. The next readdir()/getdents() that is done, will fetch the
just discarded direntries again.
In addition to this bugfix, some extra logging in send_fuse_iov() and
send_fuse_data() has been added to help diagnosing similar issues.
This change combines two commits from master:
> Commit: 20e317011af7c0f075819bf0648b225f6dc42350
> Change-Id: If2eecfcdf9c248f3820035601446d2c89ff9d1a1
> BUG: 1074023
> Signed-off-by: Niels de Vos <ndevos@redhat.com>
> Reviewed-on: http://review.gluster.org/7278
> Tested-by: Gluster Build System <jenkins@build.gluster.com>
> Reviewed-by: Xavier Hernandez <xhernandez@datalab.es>
> Reviewed-by: Anand Avati <avati@redhat.com>
>
> Commit: 76ab97169f63da78c9e83daf040d7b09766497cf
> Change-Id: Ic62d9d729a86a1a6a53ed1354fce153bac01d860
> BUG: 1074023
> Reported-by: Kaleb S. KEITHLEY <kkeithle@redhat.com>
> Signed-off-by: Niels de Vos <ndevos@redhat.com>
> Reviewed-on: http://review.gluster.org/7547
> Tested-by: Gluster Build System <jenkins@build.gluster.com>
> Reviewed-by: Anand Avati <avati@redhat.com>
Change-Id: Id847db26d861a9805041ff3367e848da7be05545
BUG: 1089934
Signed-off-by: Niels de Vos <ndevos@redhat.com>
Reviewed-on: http://review.gluster.org/7522
Tested-by: Gluster Build System <jenkins@build.gluster.com>
Reviewed-by: Xavier Hernandez <xhernandez@datalab.es>
Diffstat (limited to 'xlators')
-rw-r--r-- | xlators/mount/fuse/src/fuse-bridge.c | 63 |
1 files changed, 47 insertions, 16 deletions
diff --git a/xlators/mount/fuse/src/fuse-bridge.c b/xlators/mount/fuse/src/fuse-bridge.c index abdecbf488c..26f806e0633 100644 --- a/xlators/mount/fuse/src/fuse-bridge.c +++ b/xlators/mount/fuse/src/fuse-bridge.c @@ -186,6 +186,8 @@ send_fuse_iov (xlator_t *this, fuse_in_header_t *finh, struct iovec *iov_out, fouh->unique = finh->unique; res = writev (priv->fd, iov_out, count); + gf_log ("glusterfs-fuse", GF_LOG_TRACE, "writev() result %d/%d %s", + res, fouh->len, res == -1 ? strerror (errno) : ""); if (res == -1) return errno; @@ -215,13 +217,19 @@ send_fuse_data (xlator_t *this, fuse_in_header_t *finh, void *data, size_t size) { struct fuse_out_header fouh = {0, }; struct iovec iov_out[2]; + int ret = 0; fouh.error = 0; iov_out[0].iov_base = &fouh; iov_out[1].iov_base = data; iov_out[1].iov_len = size; - return send_fuse_iov (this, finh, iov_out, 2); + ret = send_fuse_iov (this, finh, iov_out, 2); + if (ret != 0) + gf_log ("glusterfs-fuse", GF_LOG_ERROR, "send_fuse_iov() " + "failed: %s", strerror (ret)); + + return ret; } #define send_fuse_obj(this, finh, obj) \ @@ -2501,7 +2509,8 @@ fuse_readdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, { fuse_state_t *state = NULL; fuse_in_header_t *finh = NULL; - int size = 0; + size_t size = 0; + size_t max_size = 0; char *buf = NULL; gf_dirent_t *entry = NULL; struct fuse_dirent *fde = NULL; @@ -2527,16 +2536,23 @@ fuse_readdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, frame->root->unique, op_ret, state->size, state->off); list_for_each_entry (entry, &entries->list, list) { - size += FUSE_DIRENT_ALIGN (FUSE_NAME_OFFSET + - strlen (entry->d_name)); + size_t fde_size = FUSE_DIRENT_ALIGN (FUSE_NAME_OFFSET + + strlen (entry->d_name)); + max_size += fde_size; + + if (max_size > state->size) { + /* we received too many entries to fit in the reply */ + max_size -= fde_size; + break; + } } - if (size <= 0) { - send_fuse_data (this, finh, 0, 0); - goto out; - } + if (max_size == 0) { + send_fuse_data (this, finh, 0, 0); + goto out; + } - buf = GF_CALLOC (1, size, gf_fuse_mt_char); + buf = GF_CALLOC (1, max_size, gf_fuse_mt_char); if (!buf) { gf_log ("glusterfs-fuse", GF_LOG_DEBUG, "%"PRIu64": READDIR => -1 (%s)", frame->root->unique, @@ -2550,6 +2566,9 @@ fuse_readdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, fde = (struct fuse_dirent *)(buf + size); gf_fuse_fill_dirent (entry, fde, priv->enable_ino32); size += FUSE_DIRENT_SIZE (fde); + + if (size == max_size) + break; } send_fuse_data (this, finh, buf, size); @@ -2603,7 +2622,8 @@ fuse_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this, { fuse_state_t *state = NULL; fuse_in_header_t *finh = NULL; - int size = 0; + size_t max_size = 0; + size_t size = 0; char *buf = NULL; gf_dirent_t *entry = NULL; struct fuse_direntplus *fde = NULL; @@ -2628,16 +2648,23 @@ fuse_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this, frame->root->unique, op_ret, state->size, state->off); list_for_each_entry (entry, &entries->list, list) { - size += FUSE_DIRENT_ALIGN (FUSE_NAME_OFFSET_DIRENTPLUS + - strlen (entry->d_name)); + size_t fdes = FUSE_DIRENT_ALIGN (FUSE_NAME_OFFSET_DIRENTPLUS + + strlen (entry->d_name)); + max_size += fdes; + + if (max_size > state->size) { + /* we received too many entries to fit in the reply */ + max_size -= fdes; + break; + } } - if (size <= 0) { + if (max_size == 0) { send_fuse_data (this, finh, 0, 0); goto out; } - buf = GF_CALLOC (1, size, gf_fuse_mt_char); + buf = GF_CALLOC (1, max_size, gf_fuse_mt_char); if (!buf) { gf_log ("glusterfs-fuse", GF_LOG_DEBUG, "%"PRIu64": READDIRP => -1 (%s)", frame->root->unique, @@ -2660,7 +2687,7 @@ fuse_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this, size += FUSE_DIRENTPLUS_SIZE (fde); if (!entry->inode) - continue; + goto next_entry; entry->d_stat.ia_blksize = this->ctx->page_size; gf_fuse_stat2attr (&entry->d_stat, &feo->attr, priv->enable_ino32); @@ -2668,7 +2695,7 @@ fuse_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this, linked_inode = inode_link (entry->inode, state->fd->inode, entry->d_name, &entry->d_stat); if (!linked_inode) - continue; + goto next_entry; inode_lookup (linked_inode); @@ -2686,6 +2713,10 @@ fuse_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this, calc_timeout_sec (priv->attribute_timeout); feo->attr_valid_nsec = calc_timeout_nsec (priv->attribute_timeout); + +next_entry: + if (size == max_size) + break; } send_fuse_data (this, finh, buf, size); |