Loading fs/orangefs/dir.c +93 −25 Original line number Diff line number Diff line Loading @@ -15,7 +15,14 @@ struct readdir_handle_s { }; /* * decode routine needed by kmod to make sense of the shared page for readdirs. * decode routine used by kmod to deal with the blob sent from * userspace for readdirs. The blob contains zero or more of these * sub-blobs: * __u32 - represents length of the character string that follows. * string - between 1 and ORANGEFS_NAME_MAX bytes long. * padding - (if needed) to cause the __u32 plus the string to be * eight byte aligned. * khandle - sizeof(khandle) bytes. */ static long decode_dirents(char *ptr, size_t size, struct orangefs_readdir_response_s *readdir) Loading @@ -24,54 +31,115 @@ static long decode_dirents(char *ptr, size_t size, struct orangefs_readdir_response_s *rd = (struct orangefs_readdir_response_s *) ptr; char *buf = ptr; int khandle_size = sizeof(struct orangefs_khandle); size_t offset = offsetof(struct orangefs_readdir_response_s, dirent_array); /* 8 reflects eight byte alignment */ int smallest_blob = khandle_size + 8; __u32 len; int aligned_len; int sizeof_u32 = sizeof(__u32); long ret; if (size < offsetof(struct orangefs_readdir_response_s, dirent_array)) return -EINVAL; gossip_debug(GOSSIP_DIR_DEBUG, "%s: size:%zu:\n", __func__, size); /* size is = offset on empty dirs, > offset on non-empty dirs... */ if (size < offset) { gossip_err("%s: size:%zu: offset:%zu:\n", __func__, size, offset); ret = -EINVAL; goto out; } if ((size == offset) && (readdir->orangefs_dirent_outcount != 0)) { gossip_err("%s: size:%zu: dirent_outcount:%d:\n", __func__, size, readdir->orangefs_dirent_outcount); ret = -EINVAL; goto out; } readdir->token = rd->token; readdir->orangefs_dirent_outcount = rd->orangefs_dirent_outcount; readdir->dirent_array = kcalloc(readdir->orangefs_dirent_outcount, sizeof(*readdir->dirent_array), GFP_KERNEL); if (readdir->dirent_array == NULL) return -ENOMEM; if (readdir->dirent_array == NULL) { gossip_err("%s: kcalloc failed.\n", __func__); ret = -ENOMEM; goto out; } buf += offsetof(struct orangefs_readdir_response_s, dirent_array); size -= offsetof(struct orangefs_readdir_response_s, dirent_array); buf += offset; size -= offset; for (i = 0; i < readdir->orangefs_dirent_outcount; i++) { __u32 len; if (size < 4) goto Einval; if (size < smallest_blob) { gossip_err("%s: size:%zu: smallest_blob:%d:\n", __func__, size, smallest_blob); ret = -EINVAL; goto free; } len = *(__u32 *)buf; if (len >= (unsigned)-24) goto Einval; if ((len < 1) || (len > ORANGEFS_NAME_MAX)) { gossip_err("%s: len:%d:\n", __func__, len); ret = -EINVAL; goto free; } gossip_debug(GOSSIP_DIR_DEBUG, "%s: size:%zu: len:%d:\n", __func__, size, len); readdir->dirent_array[i].d_name = buf + 4; readdir->dirent_array[i].d_name = buf + sizeof_u32; readdir->dirent_array[i].d_length = len; /* * Round 4 + len + 1, which is the encoded size plus the string * plus the null terminator to the nearest eight byte boundry. * Calculate "aligned" length of this string and its * associated __u32 descriptor. */ len = ((4 + len + 1) + 7) & ~7; if (size < len + 16) goto Einval; size -= len + 16; aligned_len = ((sizeof_u32 + len + 1) + 7) & ~7; gossip_debug(GOSSIP_DIR_DEBUG, "%s: aligned_len:%d:\n", __func__, aligned_len); buf += len; /* * The end of the blob should coincide with the end * of the last sub-blob. */ if (size < aligned_len + khandle_size) { gossip_err("%s: ran off the end of the blob.\n", __func__); ret = -EINVAL; goto free; } size -= aligned_len + khandle_size; buf += aligned_len; readdir->dirent_array[i].khandle = *(struct orangefs_khandle *) buf; buf += 16; buf += khandle_size; } return buf - ptr; Einval: ret = buf - ptr; gossip_debug(GOSSIP_DIR_DEBUG, "%s: returning:%ld:\n", __func__, ret); goto out; free: kfree(readdir->dirent_array); readdir->dirent_array = NULL; return -EINVAL; out: return ret; } static long readdir_handle_ctor(struct readdir_handle_s *rhandle, void *buf, Loading Loading
fs/orangefs/dir.c +93 −25 Original line number Diff line number Diff line Loading @@ -15,7 +15,14 @@ struct readdir_handle_s { }; /* * decode routine needed by kmod to make sense of the shared page for readdirs. * decode routine used by kmod to deal with the blob sent from * userspace for readdirs. The blob contains zero or more of these * sub-blobs: * __u32 - represents length of the character string that follows. * string - between 1 and ORANGEFS_NAME_MAX bytes long. * padding - (if needed) to cause the __u32 plus the string to be * eight byte aligned. * khandle - sizeof(khandle) bytes. */ static long decode_dirents(char *ptr, size_t size, struct orangefs_readdir_response_s *readdir) Loading @@ -24,54 +31,115 @@ static long decode_dirents(char *ptr, size_t size, struct orangefs_readdir_response_s *rd = (struct orangefs_readdir_response_s *) ptr; char *buf = ptr; int khandle_size = sizeof(struct orangefs_khandle); size_t offset = offsetof(struct orangefs_readdir_response_s, dirent_array); /* 8 reflects eight byte alignment */ int smallest_blob = khandle_size + 8; __u32 len; int aligned_len; int sizeof_u32 = sizeof(__u32); long ret; if (size < offsetof(struct orangefs_readdir_response_s, dirent_array)) return -EINVAL; gossip_debug(GOSSIP_DIR_DEBUG, "%s: size:%zu:\n", __func__, size); /* size is = offset on empty dirs, > offset on non-empty dirs... */ if (size < offset) { gossip_err("%s: size:%zu: offset:%zu:\n", __func__, size, offset); ret = -EINVAL; goto out; } if ((size == offset) && (readdir->orangefs_dirent_outcount != 0)) { gossip_err("%s: size:%zu: dirent_outcount:%d:\n", __func__, size, readdir->orangefs_dirent_outcount); ret = -EINVAL; goto out; } readdir->token = rd->token; readdir->orangefs_dirent_outcount = rd->orangefs_dirent_outcount; readdir->dirent_array = kcalloc(readdir->orangefs_dirent_outcount, sizeof(*readdir->dirent_array), GFP_KERNEL); if (readdir->dirent_array == NULL) return -ENOMEM; if (readdir->dirent_array == NULL) { gossip_err("%s: kcalloc failed.\n", __func__); ret = -ENOMEM; goto out; } buf += offsetof(struct orangefs_readdir_response_s, dirent_array); size -= offsetof(struct orangefs_readdir_response_s, dirent_array); buf += offset; size -= offset; for (i = 0; i < readdir->orangefs_dirent_outcount; i++) { __u32 len; if (size < 4) goto Einval; if (size < smallest_blob) { gossip_err("%s: size:%zu: smallest_blob:%d:\n", __func__, size, smallest_blob); ret = -EINVAL; goto free; } len = *(__u32 *)buf; if (len >= (unsigned)-24) goto Einval; if ((len < 1) || (len > ORANGEFS_NAME_MAX)) { gossip_err("%s: len:%d:\n", __func__, len); ret = -EINVAL; goto free; } gossip_debug(GOSSIP_DIR_DEBUG, "%s: size:%zu: len:%d:\n", __func__, size, len); readdir->dirent_array[i].d_name = buf + 4; readdir->dirent_array[i].d_name = buf + sizeof_u32; readdir->dirent_array[i].d_length = len; /* * Round 4 + len + 1, which is the encoded size plus the string * plus the null terminator to the nearest eight byte boundry. * Calculate "aligned" length of this string and its * associated __u32 descriptor. */ len = ((4 + len + 1) + 7) & ~7; if (size < len + 16) goto Einval; size -= len + 16; aligned_len = ((sizeof_u32 + len + 1) + 7) & ~7; gossip_debug(GOSSIP_DIR_DEBUG, "%s: aligned_len:%d:\n", __func__, aligned_len); buf += len; /* * The end of the blob should coincide with the end * of the last sub-blob. */ if (size < aligned_len + khandle_size) { gossip_err("%s: ran off the end of the blob.\n", __func__); ret = -EINVAL; goto free; } size -= aligned_len + khandle_size; buf += aligned_len; readdir->dirent_array[i].khandle = *(struct orangefs_khandle *) buf; buf += 16; buf += khandle_size; } return buf - ptr; Einval: ret = buf - ptr; gossip_debug(GOSSIP_DIR_DEBUG, "%s: returning:%ld:\n", __func__, ret); goto out; free: kfree(readdir->dirent_array); readdir->dirent_array = NULL; return -EINVAL; out: return ret; } static long readdir_handle_ctor(struct readdir_handle_s *rhandle, void *buf, Loading