summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNiels de Vos <ndevos@redhat.com>2014-04-24 13:38:31 +0200
committerNiels de Vos <ndevos@redhat.com>2014-05-08 10:58:50 -0700
commit94282fcae8ecb0d53399baa4e96f3aaa443979f0 (patch)
treeb6e072e9e26802ee7139fbf3940e71ae5f2b95d2
parentf46a3198b1a3d794c23b8ac2ab9334326c4918c8 (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>
-rw-r--r--xlators/mount/fuse/src/fuse-bridge.c63
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);