diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2015-10-09 18:11:10 -0400 |
---|---|---|
committer | Mike Marshall <hubcap@omnibond.com> | 2015-11-13 12:02:12 -0500 |
commit | 8092895f759ede31634d0f0fc85a74d970552c49 (patch) | |
tree | 800b6f04dd9cb4224e12862e88e6a842ffab7953 /fs/orangefs/dir.c | |
parent | ade1d48b788996e05fb9914dfb62993b1c279357 (diff) |
orangefs: validate the response in decode_dirents()
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Mike Marshall <hubcap@omnibond.com>
Diffstat (limited to 'fs/orangefs/dir.c')
-rw-r--r-- | fs/orangefs/dir.c | 37 |
1 files changed, 32 insertions, 5 deletions
diff --git a/fs/orangefs/dir.c b/fs/orangefs/dir.c index eb4c3d334088..3049cd61b700 100644 --- a/fs/orangefs/dir.c +++ b/fs/orangefs/dir.c @@ -17,13 +17,17 @@ struct readdir_handle_s { /* * decode routine needed by kmod to make sense of the shared page for readdirs. */ -static long decode_dirents(char *ptr, struct pvfs2_readdir_response_s *readdir) +static long decode_dirents(char *ptr, size_t size, + struct pvfs2_readdir_response_s *readdir) { int i; struct pvfs2_readdir_response_s *rd = (struct pvfs2_readdir_response_s *) ptr; char *buf = ptr; + if (size < offsetof(struct pvfs2_readdir_response_s, dirent_array)) + return -EINVAL; + readdir->token = rd->token; readdir->pvfs_dirent_outcount = rd->pvfs_dirent_outcount; readdir->dirent_array = kcalloc(readdir->pvfs_dirent_outcount, @@ -31,21 +35,43 @@ static long decode_dirents(char *ptr, struct pvfs2_readdir_response_s *readdir) GFP_KERNEL); if (readdir->dirent_array == NULL) return -ENOMEM; + buf += offsetof(struct pvfs2_readdir_response_s, dirent_array); + size -= offsetof(struct pvfs2_readdir_response_s, dirent_array); + for (i = 0; i < readdir->pvfs_dirent_outcount; i++) { - __u32 len = *(__u32 *)buf; + __u32 len; + + if (size < 4) + goto Einval; + + len = *(__u32 *)buf; + if (len >= (unsigned)-24) + goto Einval; + readdir->dirent_array[i].d_name = buf + 4; - buf += roundup8(4 + len + 1); readdir->dirent_array[i].d_length = len; + + len = roundup8(4 + len + 1); + if (size < len + 16) + goto Einval; + size -= len + 16; + + buf += len; + readdir->dirent_array[i].khandle = *(struct pvfs2_khandle *) buf; buf += 16; } return buf - ptr; +Einval: + kfree(readdir->dirent_array); + readdir->dirent_array = NULL; + return -EINVAL; } static long readdir_handle_ctor(struct readdir_handle_s *rhandle, void *buf, - int buffer_index) + size_t size, int buffer_index) { long ret; @@ -61,7 +87,7 @@ static long readdir_handle_ctor(struct readdir_handle_s *rhandle, void *buf, } rhandle->buffer_index = buffer_index; rhandle->dents_buf = buf; - ret = decode_dirents(buf, &rhandle->readdir_response); + ret = decode_dirents(buf, size, &rhandle->readdir_response); if (ret < 0) { gossip_err("Could not decode readdir from buffer %ld\n", ret); rhandle->buffer_index = -1; @@ -209,6 +235,7 @@ get_new_buffer_index: bytes_decoded = readdir_handle_ctor(&rhandle, new_op->downcall.trailer_buf, + new_op->downcall.trailer_size, buffer_index); if (bytes_decoded < 0) { gossip_err("pvfs2_readdir: Could not decode trailer buffer into a readdir response %d\n", |