diff options
author | David Howells <dhowells@redhat.com> | 2009-04-23 11:41:13 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-04-24 16:28:31 -0400 |
commit | 84baf74bf23bbe9f3deafb5d2f27e2b5dc0bc052 (patch) | |
tree | 45af84d051ca760a9f9f24aa3775e5505ccd3627 | |
parent | a5422a5111811401f7756345e4c237ff06cf6d1e (diff) |
ROMFS: romfs_lookup() shouldn't be doing a partial name comparison
romfs_lookup() should be using a routine akin to strcmp() on the backing store,
rather than one akin to strncmp(). If it uses the latter, it's liable to match
/bin/shutdown when looking up /bin/sh.
Signed-off-by: David Howells <dhowells@redhat.com>
Tested-by: Michal Simek <monstr@monstr.eu>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/romfs/internal.h | 4 | ||||
-rw-r--r-- | fs/romfs/storage.c | 67 | ||||
-rw-r--r-- | fs/romfs/super.c | 4 |
3 files changed, 53 insertions, 22 deletions
diff --git a/fs/romfs/internal.h b/fs/romfs/internal.h index 06044a9dc62d..95217b830118 100644 --- a/fs/romfs/internal.h +++ b/fs/romfs/internal.h | |||
@@ -43,5 +43,5 @@ extern int romfs_dev_read(struct super_block *sb, unsigned long pos, | |||
43 | void *buf, size_t buflen); | 43 | void *buf, size_t buflen); |
44 | extern ssize_t romfs_dev_strnlen(struct super_block *sb, | 44 | extern ssize_t romfs_dev_strnlen(struct super_block *sb, |
45 | unsigned long pos, size_t maxlen); | 45 | unsigned long pos, size_t maxlen); |
46 | extern int romfs_dev_strncmp(struct super_block *sb, unsigned long pos, | 46 | extern int romfs_dev_strcmp(struct super_block *sb, unsigned long pos, |
47 | const char *str, size_t size); | 47 | const char *str, size_t size); |
diff --git a/fs/romfs/storage.c b/fs/romfs/storage.c index 7e3e1e12a081..66ce9ddfe504 100644 --- a/fs/romfs/storage.c +++ b/fs/romfs/storage.c | |||
@@ -67,26 +67,35 @@ static ssize_t romfs_mtd_strnlen(struct super_block *sb, | |||
67 | * compare a string to one in a romfs image on MTD | 67 | * compare a string to one in a romfs image on MTD |
68 | * - return 1 if matched, 0 if differ, -ve if error | 68 | * - return 1 if matched, 0 if differ, -ve if error |
69 | */ | 69 | */ |
70 | static int romfs_mtd_strncmp(struct super_block *sb, unsigned long pos, | 70 | static int romfs_mtd_strcmp(struct super_block *sb, unsigned long pos, |
71 | const char *str, size_t size) | 71 | const char *str, size_t size) |
72 | { | 72 | { |
73 | u_char buf[16]; | 73 | u_char buf[17]; |
74 | size_t len, segment; | 74 | size_t len, segment; |
75 | int ret; | 75 | int ret; |
76 | 76 | ||
77 | /* scan the string up to 16 bytes at a time */ | 77 | /* scan the string up to 16 bytes at a time, and attempt to grab the |
78 | * trailing NUL whilst we're at it */ | ||
79 | buf[0] = 0xff; | ||
80 | |||
78 | while (size > 0) { | 81 | while (size > 0) { |
79 | segment = min_t(size_t, size, 16); | 82 | segment = min_t(size_t, size + 1, 17); |
80 | ret = ROMFS_MTD_READ(sb, pos, segment, &len, buf); | 83 | ret = ROMFS_MTD_READ(sb, pos, segment, &len, buf); |
81 | if (ret < 0) | 84 | if (ret < 0) |
82 | return ret; | 85 | return ret; |
86 | len--; | ||
83 | if (memcmp(buf, str, len) != 0) | 87 | if (memcmp(buf, str, len) != 0) |
84 | return 0; | 88 | return 0; |
89 | buf[0] = buf[len]; | ||
85 | size -= len; | 90 | size -= len; |
86 | pos += len; | 91 | pos += len; |
87 | str += len; | 92 | str += len; |
88 | } | 93 | } |
89 | 94 | ||
95 | /* check the trailing NUL was */ | ||
96 | if (buf[0]) | ||
97 | return 0; | ||
98 | |||
90 | return 1; | 99 | return 1; |
91 | } | 100 | } |
92 | #endif /* CONFIG_ROMFS_ON_MTD */ | 101 | #endif /* CONFIG_ROMFS_ON_MTD */ |
@@ -154,28 +163,48 @@ static ssize_t romfs_blk_strnlen(struct super_block *sb, | |||
154 | * compare a string to one in a romfs image on a block device | 163 | * compare a string to one in a romfs image on a block device |
155 | * - return 1 if matched, 0 if differ, -ve if error | 164 | * - return 1 if matched, 0 if differ, -ve if error |
156 | */ | 165 | */ |
157 | static int romfs_blk_strncmp(struct super_block *sb, unsigned long pos, | 166 | static int romfs_blk_strcmp(struct super_block *sb, unsigned long pos, |
158 | const char *str, size_t size) | 167 | const char *str, size_t size) |
159 | { | 168 | { |
160 | struct buffer_head *bh; | 169 | struct buffer_head *bh; |
161 | unsigned long offset; | 170 | unsigned long offset; |
162 | size_t segment; | 171 | size_t segment; |
163 | bool x; | 172 | bool matched, terminated = false; |
164 | 173 | ||
165 | /* scan the string up to 16 bytes at a time */ | 174 | /* compare string up to a block at a time */ |
166 | while (size > 0) { | 175 | while (size > 0) { |
167 | offset = pos & (ROMBSIZE - 1); | 176 | offset = pos & (ROMBSIZE - 1); |
168 | segment = min_t(size_t, size, ROMBSIZE - offset); | 177 | segment = min_t(size_t, size, ROMBSIZE - offset); |
169 | bh = sb_bread(sb, pos >> ROMBSBITS); | 178 | bh = sb_bread(sb, pos >> ROMBSBITS); |
170 | if (!bh) | 179 | if (!bh) |
171 | return -EIO; | 180 | return -EIO; |
172 | x = (memcmp(bh->b_data + offset, str, segment) != 0); | 181 | matched = (memcmp(bh->b_data + offset, str, segment) == 0); |
173 | brelse(bh); | 182 | |
174 | if (x) | ||
175 | return 0; | ||
176 | size -= segment; | 183 | size -= segment; |
177 | pos += segment; | 184 | pos += segment; |
178 | str += segment; | 185 | str += segment; |
186 | if (matched && size == 0 && offset + segment < ROMBSIZE) { | ||
187 | if (!bh->b_data[offset + segment]) | ||
188 | terminated = true; | ||
189 | else | ||
190 | matched = false; | ||
191 | } | ||
192 | brelse(bh); | ||
193 | if (!matched) | ||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | if (!terminated) { | ||
198 | /* the terminating NUL must be on the first byte of the next | ||
199 | * block */ | ||
200 | BUG_ON((pos & (ROMBSIZE - 1)) != 0); | ||
201 | bh = sb_bread(sb, pos >> ROMBSBITS); | ||
202 | if (!bh) | ||
203 | return -EIO; | ||
204 | matched = !bh->b_data[0]; | ||
205 | brelse(bh); | ||
206 | if (!matched) | ||
207 | return 0; | ||
179 | } | 208 | } |
180 | 209 | ||
181 | return 1; | 210 | return 1; |
@@ -234,10 +263,12 @@ ssize_t romfs_dev_strnlen(struct super_block *sb, | |||
234 | 263 | ||
235 | /* | 264 | /* |
236 | * compare a string to one in romfs | 265 | * compare a string to one in romfs |
266 | * - the string to be compared to, str, may not be NUL-terminated; instead the | ||
267 | * string is of the specified size | ||
237 | * - return 1 if matched, 0 if differ, -ve if error | 268 | * - return 1 if matched, 0 if differ, -ve if error |
238 | */ | 269 | */ |
239 | int romfs_dev_strncmp(struct super_block *sb, unsigned long pos, | 270 | int romfs_dev_strcmp(struct super_block *sb, unsigned long pos, |
240 | const char *str, size_t size) | 271 | const char *str, size_t size) |
241 | { | 272 | { |
242 | size_t limit; | 273 | size_t limit; |
243 | 274 | ||
@@ -246,16 +277,16 @@ int romfs_dev_strncmp(struct super_block *sb, unsigned long pos, | |||
246 | return -EIO; | 277 | return -EIO; |
247 | if (size > ROMFS_MAXFN) | 278 | if (size > ROMFS_MAXFN) |
248 | return -ENAMETOOLONG; | 279 | return -ENAMETOOLONG; |
249 | if (size > limit - pos) | 280 | if (size + 1 > limit - pos) |
250 | return -EIO; | 281 | return -EIO; |
251 | 282 | ||
252 | #ifdef CONFIG_ROMFS_ON_MTD | 283 | #ifdef CONFIG_ROMFS_ON_MTD |
253 | if (sb->s_mtd) | 284 | if (sb->s_mtd) |
254 | return romfs_mtd_strncmp(sb, pos, str, size); | 285 | return romfs_mtd_strcmp(sb, pos, str, size); |
255 | #endif | 286 | #endif |
256 | #ifdef CONFIG_ROMFS_ON_BLOCK | 287 | #ifdef CONFIG_ROMFS_ON_BLOCK |
257 | if (sb->s_bdev) | 288 | if (sb->s_bdev) |
258 | return romfs_blk_strncmp(sb, pos, str, size); | 289 | return romfs_blk_strcmp(sb, pos, str, size); |
259 | #endif | 290 | #endif |
260 | return -EIO; | 291 | return -EIO; |
261 | } | 292 | } |
diff --git a/fs/romfs/super.c b/fs/romfs/super.c index 10ca7d984a8b..c53b5ef8a02f 100644 --- a/fs/romfs/super.c +++ b/fs/romfs/super.c | |||
@@ -240,8 +240,8 @@ static struct dentry *romfs_lookup(struct inode *dir, struct dentry *dentry, | |||
240 | goto error; | 240 | goto error; |
241 | 241 | ||
242 | /* try to match the first 16 bytes of name */ | 242 | /* try to match the first 16 bytes of name */ |
243 | ret = romfs_dev_strncmp(dir->i_sb, offset + ROMFH_SIZE, name, | 243 | ret = romfs_dev_strcmp(dir->i_sb, offset + ROMFH_SIZE, name, |
244 | len); | 244 | len); |
245 | if (ret < 0) | 245 | if (ret < 0) |
246 | goto error; | 246 | goto error; |
247 | if (ret == 1) | 247 | if (ret == 1) |