Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

David Howells and committed by
Linus Torvalds
84baf74b a5422a51

+53 -22
+2 -2
fs/romfs/internal.h
··· 43 43 void *buf, size_t buflen); 44 44 extern ssize_t romfs_dev_strnlen(struct super_block *sb, 45 45 unsigned long pos, size_t maxlen); 46 - extern int romfs_dev_strncmp(struct super_block *sb, unsigned long pos, 47 - const char *str, size_t size); 46 + extern int romfs_dev_strcmp(struct super_block *sb, unsigned long pos, 47 + const char *str, size_t size);
+49 -18
fs/romfs/storage.c
··· 67 67 * compare a string to one in a romfs image on MTD 68 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, 71 - const char *str, size_t size) 70 + static int romfs_mtd_strcmp(struct super_block *sb, unsigned long pos, 71 + const char *str, size_t size) 72 72 { 73 - u_char buf[16]; 73 + u_char buf[17]; 74 74 size_t len, segment; 75 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 81 while (size > 0) { 79 - segment = min_t(size_t, size, 16); 82 + segment = min_t(size_t, size + 1, 17); 80 83 ret = ROMFS_MTD_READ(sb, pos, segment, &len, buf); 81 84 if (ret < 0) 82 85 return ret; 86 + len--; 83 87 if (memcmp(buf, str, len) != 0) 84 88 return 0; 89 + buf[0] = buf[len]; 85 90 size -= len; 86 91 pos += len; 87 92 str += len; 88 93 } 94 + 95 + /* check the trailing NUL was */ 96 + if (buf[0]) 97 + return 0; 89 98 90 99 return 1; 91 100 } ··· 163 154 * compare a string to one in a romfs image on a block device 164 155 * - return 1 if matched, 0 if differ, -ve if error 165 156 */ 166 - static int romfs_blk_strncmp(struct super_block *sb, unsigned long pos, 167 - const char *str, size_t size) 157 + static int romfs_blk_strcmp(struct super_block *sb, unsigned long pos, 158 + const char *str, size_t size) 168 159 { 169 160 struct buffer_head *bh; 170 161 unsigned long offset; 171 162 size_t segment; 172 - bool x; 163 + bool matched, terminated = false; 173 164 174 - /* scan the string up to 16 bytes at a time */ 165 + /* compare string up to a block at a time */ 175 166 while (size > 0) { 176 167 offset = pos & (ROMBSIZE - 1); 177 168 segment = min_t(size_t, size, ROMBSIZE - offset); 178 169 bh = sb_bread(sb, pos >> ROMBSBITS); 179 170 if (!bh) 180 171 return -EIO; 181 - x = (memcmp(bh->b_data + offset, str, segment) != 0); 182 - brelse(bh); 183 - if (x) 184 - return 0; 172 + matched = (memcmp(bh->b_data + offset, str, segment) == 0); 173 + 185 174 size -= segment; 186 175 pos += segment; 187 176 str += segment; 177 + if (matched && size == 0 && offset + segment < ROMBSIZE) { 178 + if (!bh->b_data[offset + segment]) 179 + terminated = true; 180 + else 181 + matched = false; 182 + } 183 + brelse(bh); 184 + if (!matched) 185 + return 0; 186 + } 187 + 188 + if (!terminated) { 189 + /* the terminating NUL must be on the first byte of the next 190 + * block */ 191 + BUG_ON((pos & (ROMBSIZE - 1)) != 0); 192 + bh = sb_bread(sb, pos >> ROMBSBITS); 193 + if (!bh) 194 + return -EIO; 195 + matched = !bh->b_data[0]; 196 + brelse(bh); 197 + if (!matched) 198 + return 0; 188 199 } 189 200 190 201 return 1; ··· 263 234 264 235 /* 265 236 * compare a string to one in romfs 237 + * - the string to be compared to, str, may not be NUL-terminated; instead the 238 + * string is of the specified size 266 239 * - return 1 if matched, 0 if differ, -ve if error 267 240 */ 268 - int romfs_dev_strncmp(struct super_block *sb, unsigned long pos, 269 - const char *str, size_t size) 241 + int romfs_dev_strcmp(struct super_block *sb, unsigned long pos, 242 + const char *str, size_t size) 270 243 { 271 244 size_t limit; 272 245 ··· 277 246 return -EIO; 278 247 if (size > ROMFS_MAXFN) 279 248 return -ENAMETOOLONG; 280 - if (size > limit - pos) 249 + if (size + 1 > limit - pos) 281 250 return -EIO; 282 251 283 252 #ifdef CONFIG_ROMFS_ON_MTD 284 253 if (sb->s_mtd) 285 - return romfs_mtd_strncmp(sb, pos, str, size); 254 + return romfs_mtd_strcmp(sb, pos, str, size); 286 255 #endif 287 256 #ifdef CONFIG_ROMFS_ON_BLOCK 288 257 if (sb->s_bdev) 289 - return romfs_blk_strncmp(sb, pos, str, size); 258 + return romfs_blk_strcmp(sb, pos, str, size); 290 259 #endif 291 260 return -EIO; 292 261 }
+2 -2
fs/romfs/super.c
··· 240 240 goto error; 241 241 242 242 /* try to match the first 16 bytes of name */ 243 - ret = romfs_dev_strncmp(dir->i_sb, offset + ROMFH_SIZE, name, 244 - len); 243 + ret = romfs_dev_strcmp(dir->i_sb, offset + ROMFH_SIZE, name, 244 + len); 245 245 if (ret < 0) 246 246 goto error; 247 247 if (ret == 1)