diff options
author | Niels de Vos <ndevos@redhat.com> | 2014-03-27 20:34:44 +0100 |
---|---|---|
committer | Anand Avati <avati@redhat.com> | 2014-04-21 10:25:21 -0700 |
commit | 20e317011af7c0f075819bf0648b225f6dc42350 (patch) | |
tree | 35576dbeb6202d529e4a8c0a3ae8faa168299a59 | |
parent | 2da51737c49f7917a974bdf9e6e566307583ad16 (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.
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>
-rw-r--r-- | xlators/mount/fuse/src/fuse-bridge.c | 60 |
1 files changed, 46 insertions, 14 deletions
diff --git a/xlators/mount/fuse/src/fuse-bridge.c b/xlators/mount/fuse/src/fuse-bridge.c index cbf256e8b85..fd44c4fb5f5 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) \ @@ -2524,6 +2532,7 @@ 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; + int max_size = 0; char *buf = NULL; gf_dirent_t *entry = NULL; struct fuse_dirent *fde = NULL; @@ -2549,16 +2558,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)); + max_size += FUSE_DIRENT_ALIGN (FUSE_NAME_OFFSET + + strlen (entry->d_name)); + + if (max_size > state->size) { + /* we received to many entries to fit in the request */ + max_size -= FUSE_DIRENT_ALIGN (FUSE_NAME_OFFSET + + strlen (entry->d_name)); + 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, @@ -2572,6 +2588,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); @@ -2625,6 +2644,7 @@ fuse_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this, { fuse_state_t *state = NULL; fuse_in_header_t *finh = NULL; + int max_size = 0; int size = 0; char *buf = NULL; gf_dirent_t *entry = NULL; @@ -2650,16 +2670,24 @@ 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)); + max_size += FUSE_DIRENT_ALIGN (FUSE_NAME_OFFSET_DIRENTPLUS + + strlen (entry->d_name)); + + if (max_size > state->size) { + /* we received to many entries to fit in the reply */ + max_size -= FUSE_DIRENT_ALIGN ( + FUSE_NAME_OFFSET_DIRENTPLUS + + strlen (entry->d_name)); + 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, @@ -2682,7 +2710,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); @@ -2690,7 +2718,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); @@ -2708,6 +2736,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); |