aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Marshall <hubcap@omnibond.com>2016-01-15 13:10:52 -0500
committerMike Marshall <hubcap@omnibond.com>2016-01-15 13:10:52 -0500
commit1808f8cc6cb2842c53147eccfd5e88044d0d22a6 (patch)
tree17bd3aaecf09725f16adefff8a4738b5d4a53fe0
parentfcac9d571567e8bf952616f4a271eea5b4b407ea (diff)
Orangefs: add verification to decode_dirents
Also add comments to decode_dirents and make it more readable. Signed-off-by: Mike Marshall <hubcap@omnibond.com>
-rw-r--r--fs/orangefs/dir.c118
1 files changed, 93 insertions, 25 deletions
diff --git a/fs/orangefs/dir.c b/fs/orangefs/dir.c
index 58558e37fb8a..6f5836d6a7a3 100644
--- a/fs/orangefs/dir.c
+++ b/fs/orangefs/dir.c
@@ -15,7 +15,14 @@ struct readdir_handle_s {
15}; 15};
16 16
17/* 17/*
18 * decode routine needed by kmod to make sense of the shared page for readdirs. 18 * decode routine used by kmod to deal with the blob sent from
19 * userspace for readdirs. The blob contains zero or more of these
20 * sub-blobs:
21 * __u32 - represents length of the character string that follows.
22 * string - between 1 and ORANGEFS_NAME_MAX bytes long.
23 * padding - (if needed) to cause the __u32 plus the string to be
24 * eight byte aligned.
25 * khandle - sizeof(khandle) bytes.
19 */ 26 */
20static long decode_dirents(char *ptr, size_t size, 27static long decode_dirents(char *ptr, size_t size,
21 struct orangefs_readdir_response_s *readdir) 28 struct orangefs_readdir_response_s *readdir)
@@ -24,54 +31,115 @@ static long decode_dirents(char *ptr, size_t size,
24 struct orangefs_readdir_response_s *rd = 31 struct orangefs_readdir_response_s *rd =
25 (struct orangefs_readdir_response_s *) ptr; 32 (struct orangefs_readdir_response_s *) ptr;
26 char *buf = ptr; 33 char *buf = ptr;
34 int khandle_size = sizeof(struct orangefs_khandle);
35 size_t offset = offsetof(struct orangefs_readdir_response_s,
36 dirent_array);
37 /* 8 reflects eight byte alignment */
38 int smallest_blob = khandle_size + 8;
39 __u32 len;
40 int aligned_len;
41 int sizeof_u32 = sizeof(__u32);
42 long ret;
27 43
28 if (size < offsetof(struct orangefs_readdir_response_s, dirent_array)) 44 gossip_debug(GOSSIP_DIR_DEBUG, "%s: size:%zu:\n", __func__, size);
29 return -EINVAL; 45
46 /* size is = offset on empty dirs, > offset on non-empty dirs... */
47 if (size < offset) {
48 gossip_err("%s: size:%zu: offset:%zu:\n",
49 __func__,
50 size,
51 offset);
52 ret = -EINVAL;
53 goto out;
54 }
55
56 if ((size == offset) && (readdir->orangefs_dirent_outcount != 0)) {
57 gossip_err("%s: size:%zu: dirent_outcount:%d:\n",
58 __func__,
59 size,
60 readdir->orangefs_dirent_outcount);
61 ret = -EINVAL;
62 goto out;
63 }
30 64
31 readdir->token = rd->token; 65 readdir->token = rd->token;
32 readdir->orangefs_dirent_outcount = rd->orangefs_dirent_outcount; 66 readdir->orangefs_dirent_outcount = rd->orangefs_dirent_outcount;
33 readdir->dirent_array = kcalloc(readdir->orangefs_dirent_outcount, 67 readdir->dirent_array = kcalloc(readdir->orangefs_dirent_outcount,
34 sizeof(*readdir->dirent_array), 68 sizeof(*readdir->dirent_array),
35 GFP_KERNEL); 69 GFP_KERNEL);
36 if (readdir->dirent_array == NULL) 70 if (readdir->dirent_array == NULL) {
37 return -ENOMEM; 71 gossip_err("%s: kcalloc failed.\n", __func__);
72 ret = -ENOMEM;
73 goto out;
74 }
38 75
39 buf += offsetof(struct orangefs_readdir_response_s, dirent_array); 76 buf += offset;
40 size -= offsetof(struct orangefs_readdir_response_s, dirent_array); 77 size -= offset;
41 78
42 for (i = 0; i < readdir->orangefs_dirent_outcount; i++) { 79 for (i = 0; i < readdir->orangefs_dirent_outcount; i++) {
43 __u32 len; 80 if (size < smallest_blob) {
44 81 gossip_err("%s: size:%zu: smallest_blob:%d:\n",
45 if (size < 4) 82 __func__,
46 goto Einval; 83 size,
84 smallest_blob);
85 ret = -EINVAL;
86 goto free;
87 }
47 88
48 len = *(__u32 *)buf; 89 len = *(__u32 *)buf;
49 if (len >= (unsigned)-24) 90 if ((len < 1) || (len > ORANGEFS_NAME_MAX)) {
50 goto Einval; 91 gossip_err("%s: len:%d:\n", __func__, len);
92 ret = -EINVAL;
93 goto free;
94 }
51 95
52 readdir->dirent_array[i].d_name = buf + 4; 96 gossip_debug(GOSSIP_DIR_DEBUG,
97 "%s: size:%zu: len:%d:\n",
98 __func__,
99 size,
100 len);
101
102 readdir->dirent_array[i].d_name = buf + sizeof_u32;
53 readdir->dirent_array[i].d_length = len; 103 readdir->dirent_array[i].d_length = len;
54 104
55 /* 105 /*
56 * Round 4 + len + 1, which is the encoded size plus the string 106 * Calculate "aligned" length of this string and its
57 * plus the null terminator to the nearest eight byte boundry. 107 * associated __u32 descriptor.
58 */ 108 */
59 len = ((4 + len + 1) + 7) & ~7; 109 aligned_len = ((sizeof_u32 + len + 1) + 7) & ~7;
60 if (size < len + 16) 110 gossip_debug(GOSSIP_DIR_DEBUG,
61 goto Einval; 111 "%s: aligned_len:%d:\n",
62 size -= len + 16; 112 __func__,
113 aligned_len);
63 114
64 buf += len; 115 /*
116 * The end of the blob should coincide with the end
117 * of the last sub-blob.
118 */
119 if (size < aligned_len + khandle_size) {
120 gossip_err("%s: ran off the end of the blob.\n",
121 __func__);
122 ret = -EINVAL;
123 goto free;
124 }
125 size -= aligned_len + khandle_size;
126
127 buf += aligned_len;
65 128
66 readdir->dirent_array[i].khandle = 129 readdir->dirent_array[i].khandle =
67 *(struct orangefs_khandle *) buf; 130 *(struct orangefs_khandle *) buf;
68 buf += 16; 131 buf += khandle_size;
69 } 132 }
70 return buf - ptr; 133 ret = buf - ptr;
71Einval: 134 gossip_debug(GOSSIP_DIR_DEBUG, "%s: returning:%ld:\n", __func__, ret);
135 goto out;
136
137free:
72 kfree(readdir->dirent_array); 138 kfree(readdir->dirent_array);
73 readdir->dirent_array = NULL; 139 readdir->dirent_array = NULL;
74 return -EINVAL; 140
141out:
142 return ret;
75} 143}
76 144
77static long readdir_handle_ctor(struct readdir_handle_s *rhandle, void *buf, 145static long readdir_handle_ctor(struct readdir_handle_s *rhandle, void *buf,