diff options
author | Mike Marshall <hubcap@omnibond.com> | 2016-01-15 13:10:52 -0500 |
---|---|---|
committer | Mike Marshall <hubcap@omnibond.com> | 2016-01-15 13:10:52 -0500 |
commit | 1808f8cc6cb2842c53147eccfd5e88044d0d22a6 (patch) | |
tree | 17bd3aaecf09725f16adefff8a4738b5d4a53fe0 | |
parent | fcac9d571567e8bf952616f4a271eea5b4b407ea (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.c | 118 |
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 | */ |
20 | static long decode_dirents(char *ptr, size_t size, | 27 | static 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; |
71 | Einval: | 134 | gossip_debug(GOSSIP_DIR_DEBUG, "%s: returning:%ld:\n", __func__, ret); |
135 | goto out; | ||
136 | |||
137 | free: | ||
72 | kfree(readdir->dirent_array); | 138 | kfree(readdir->dirent_array); |
73 | readdir->dirent_array = NULL; | 139 | readdir->dirent_array = NULL; |
74 | return -EINVAL; | 140 | |
141 | out: | ||
142 | return ret; | ||
75 | } | 143 | } |
76 | 144 | ||
77 | static long readdir_handle_ctor(struct readdir_handle_s *rhandle, void *buf, | 145 | static long readdir_handle_ctor(struct readdir_handle_s *rhandle, void *buf, |