diff options
author | Jan Kara <jack@suse.cz> | 2014-12-18 16:37:50 -0500 |
---|---|---|
committer | Jan Kara <jack@suse.cz> | 2014-12-19 08:12:08 -0500 |
commit | 0e5cc9a40ada6046e6bc3bdfcd0c0d7e4b706b14 (patch) | |
tree | 6a9a8bef5942cb6b18ae977fd1be24c59cebe71c /fs | |
parent | a1d47b262952a45aae62bd49cfaf33dd76c11a2c (diff) |
udf: Check path length when reading symlink
Symlink reading code does not check whether the resulting path fits into
the page provided by the generic code. This isn't as easy as just
checking the symlink size because of various encoding conversions we
perform on path. So we have to check whether there is still enough space
in the buffer on the fly.
CC: stable@vger.kernel.org
Reported-by: Carl Henrik Lunde <chlunde@ping.uio.no>
Signed-off-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/udf/dir.c | 3 | ||||
-rw-r--r-- | fs/udf/namei.c | 3 | ||||
-rw-r--r-- | fs/udf/symlink.c | 31 | ||||
-rw-r--r-- | fs/udf/udfdecl.h | 3 | ||||
-rw-r--r-- | fs/udf/unicode.c | 28 |
5 files changed, 48 insertions, 20 deletions
diff --git a/fs/udf/dir.c b/fs/udf/dir.c index a012c51caffd..a7690b46ce0a 100644 --- a/fs/udf/dir.c +++ b/fs/udf/dir.c | |||
@@ -167,7 +167,8 @@ static int udf_readdir(struct file *file, struct dir_context *ctx) | |||
167 | continue; | 167 | continue; |
168 | } | 168 | } |
169 | 169 | ||
170 | flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi); | 170 | flen = udf_get_filename(dir->i_sb, nameptr, lfi, fname, |
171 | UDF_NAME_LEN); | ||
171 | if (!flen) | 172 | if (!flen) |
172 | continue; | 173 | continue; |
173 | 174 | ||
diff --git a/fs/udf/namei.c b/fs/udf/namei.c index c12e260fd6c4..6ff19b54b51f 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c | |||
@@ -233,7 +233,8 @@ static struct fileIdentDesc *udf_find_entry(struct inode *dir, | |||
233 | if (!lfi) | 233 | if (!lfi) |
234 | continue; | 234 | continue; |
235 | 235 | ||
236 | flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi); | 236 | flen = udf_get_filename(dir->i_sb, nameptr, lfi, fname, |
237 | UDF_NAME_LEN); | ||
237 | if (flen && udf_match(flen, fname, child->len, child->name)) | 238 | if (flen && udf_match(flen, fname, child->len, child->name)) |
238 | goto out_ok; | 239 | goto out_ok; |
239 | } | 240 | } |
diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c index c3aa6fafd6cf..0f1b3a2654b9 100644 --- a/fs/udf/symlink.c +++ b/fs/udf/symlink.c | |||
@@ -30,13 +30,16 @@ | |||
30 | #include <linux/buffer_head.h> | 30 | #include <linux/buffer_head.h> |
31 | #include "udf_i.h" | 31 | #include "udf_i.h" |
32 | 32 | ||
33 | static void udf_pc_to_char(struct super_block *sb, unsigned char *from, | 33 | static int udf_pc_to_char(struct super_block *sb, unsigned char *from, |
34 | int fromlen, unsigned char *to) | 34 | int fromlen, unsigned char *to, int tolen) |
35 | { | 35 | { |
36 | struct pathComponent *pc; | 36 | struct pathComponent *pc; |
37 | int elen = 0; | 37 | int elen = 0; |
38 | int comp_len; | ||
38 | unsigned char *p = to; | 39 | unsigned char *p = to; |
39 | 40 | ||
41 | /* Reserve one byte for terminating \0 */ | ||
42 | tolen--; | ||
40 | while (elen < fromlen) { | 43 | while (elen < fromlen) { |
41 | pc = (struct pathComponent *)(from + elen); | 44 | pc = (struct pathComponent *)(from + elen); |
42 | switch (pc->componentType) { | 45 | switch (pc->componentType) { |
@@ -49,22 +52,37 @@ static void udf_pc_to_char(struct super_block *sb, unsigned char *from, | |||
49 | break; | 52 | break; |
50 | /* Fall through */ | 53 | /* Fall through */ |
51 | case 2: | 54 | case 2: |
55 | if (tolen == 0) | ||
56 | return -ENAMETOOLONG; | ||
52 | p = to; | 57 | p = to; |
53 | *p++ = '/'; | 58 | *p++ = '/'; |
59 | tolen--; | ||
54 | break; | 60 | break; |
55 | case 3: | 61 | case 3: |
62 | if (tolen < 3) | ||
63 | return -ENAMETOOLONG; | ||
56 | memcpy(p, "../", 3); | 64 | memcpy(p, "../", 3); |
57 | p += 3; | 65 | p += 3; |
66 | tolen -= 3; | ||
58 | break; | 67 | break; |
59 | case 4: | 68 | case 4: |
69 | if (tolen < 2) | ||
70 | return -ENAMETOOLONG; | ||
60 | memcpy(p, "./", 2); | 71 | memcpy(p, "./", 2); |
61 | p += 2; | 72 | p += 2; |
73 | tolen -= 2; | ||
62 | /* that would be . - just ignore */ | 74 | /* that would be . - just ignore */ |
63 | break; | 75 | break; |
64 | case 5: | 76 | case 5: |
65 | p += udf_get_filename(sb, pc->componentIdent, p, | 77 | comp_len = udf_get_filename(sb, pc->componentIdent, |
66 | pc->lengthComponentIdent); | 78 | pc->lengthComponentIdent, |
79 | p, tolen); | ||
80 | p += comp_len; | ||
81 | tolen -= comp_len; | ||
82 | if (tolen == 0) | ||
83 | return -ENAMETOOLONG; | ||
67 | *p++ = '/'; | 84 | *p++ = '/'; |
85 | tolen--; | ||
68 | break; | 86 | break; |
69 | } | 87 | } |
70 | elen += sizeof(struct pathComponent) + pc->lengthComponentIdent; | 88 | elen += sizeof(struct pathComponent) + pc->lengthComponentIdent; |
@@ -73,6 +91,7 @@ static void udf_pc_to_char(struct super_block *sb, unsigned char *from, | |||
73 | p[-1] = '\0'; | 91 | p[-1] = '\0'; |
74 | else | 92 | else |
75 | p[0] = '\0'; | 93 | p[0] = '\0'; |
94 | return 0; | ||
76 | } | 95 | } |
77 | 96 | ||
78 | static int udf_symlink_filler(struct file *file, struct page *page) | 97 | static int udf_symlink_filler(struct file *file, struct page *page) |
@@ -108,8 +127,10 @@ static int udf_symlink_filler(struct file *file, struct page *page) | |||
108 | symlink = bh->b_data; | 127 | symlink = bh->b_data; |
109 | } | 128 | } |
110 | 129 | ||
111 | udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p); | 130 | err = udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p, PAGE_SIZE); |
112 | brelse(bh); | 131 | brelse(bh); |
132 | if (err) | ||
133 | goto out_unlock_inode; | ||
113 | 134 | ||
114 | up_read(&iinfo->i_data_sem); | 135 | up_read(&iinfo->i_data_sem); |
115 | SetPageUptodate(page); | 136 | SetPageUptodate(page); |
diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h index 1cc3c993ebd0..47bb3f5ca360 100644 --- a/fs/udf/udfdecl.h +++ b/fs/udf/udfdecl.h | |||
@@ -211,7 +211,8 @@ udf_get_lb_pblock(struct super_block *sb, struct kernel_lb_addr *loc, | |||
211 | } | 211 | } |
212 | 212 | ||
213 | /* unicode.c */ | 213 | /* unicode.c */ |
214 | extern int udf_get_filename(struct super_block *, uint8_t *, uint8_t *, int); | 214 | extern int udf_get_filename(struct super_block *, uint8_t *, int, uint8_t *, |
215 | int); | ||
215 | extern int udf_put_filename(struct super_block *, const uint8_t *, uint8_t *, | 216 | extern int udf_put_filename(struct super_block *, const uint8_t *, uint8_t *, |
216 | int); | 217 | int); |
217 | extern int udf_build_ustr(struct ustr *, dstring *, int); | 218 | extern int udf_build_ustr(struct ustr *, dstring *, int); |
diff --git a/fs/udf/unicode.c b/fs/udf/unicode.c index afd470e588ff..b84fee372734 100644 --- a/fs/udf/unicode.c +++ b/fs/udf/unicode.c | |||
@@ -28,7 +28,8 @@ | |||
28 | 28 | ||
29 | #include "udf_sb.h" | 29 | #include "udf_sb.h" |
30 | 30 | ||
31 | static int udf_translate_to_linux(uint8_t *, uint8_t *, int, uint8_t *, int); | 31 | static int udf_translate_to_linux(uint8_t *, int, uint8_t *, int, uint8_t *, |
32 | int); | ||
32 | 33 | ||
33 | static int udf_char_to_ustr(struct ustr *dest, const uint8_t *src, int strlen) | 34 | static int udf_char_to_ustr(struct ustr *dest, const uint8_t *src, int strlen) |
34 | { | 35 | { |
@@ -333,8 +334,8 @@ try_again: | |||
333 | return u_len + 1; | 334 | return u_len + 1; |
334 | } | 335 | } |
335 | 336 | ||
336 | int udf_get_filename(struct super_block *sb, uint8_t *sname, uint8_t *dname, | 337 | int udf_get_filename(struct super_block *sb, uint8_t *sname, int slen, |
337 | int flen) | 338 | uint8_t *dname, int dlen) |
338 | { | 339 | { |
339 | struct ustr *filename, *unifilename; | 340 | struct ustr *filename, *unifilename; |
340 | int len = 0; | 341 | int len = 0; |
@@ -347,7 +348,7 @@ int udf_get_filename(struct super_block *sb, uint8_t *sname, uint8_t *dname, | |||
347 | if (!unifilename) | 348 | if (!unifilename) |
348 | goto out1; | 349 | goto out1; |
349 | 350 | ||
350 | if (udf_build_ustr_exact(unifilename, sname, flen)) | 351 | if (udf_build_ustr_exact(unifilename, sname, slen)) |
351 | goto out2; | 352 | goto out2; |
352 | 353 | ||
353 | if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) { | 354 | if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) { |
@@ -366,7 +367,8 @@ int udf_get_filename(struct super_block *sb, uint8_t *sname, uint8_t *dname, | |||
366 | } else | 367 | } else |
367 | goto out2; | 368 | goto out2; |
368 | 369 | ||
369 | len = udf_translate_to_linux(dname, filename->u_name, filename->u_len, | 370 | len = udf_translate_to_linux(dname, dlen, |
371 | filename->u_name, filename->u_len, | ||
370 | unifilename->u_name, unifilename->u_len); | 372 | unifilename->u_name, unifilename->u_len); |
371 | out2: | 373 | out2: |
372 | kfree(unifilename); | 374 | kfree(unifilename); |
@@ -403,10 +405,12 @@ int udf_put_filename(struct super_block *sb, const uint8_t *sname, | |||
403 | #define EXT_MARK '.' | 405 | #define EXT_MARK '.' |
404 | #define CRC_MARK '#' | 406 | #define CRC_MARK '#' |
405 | #define EXT_SIZE 5 | 407 | #define EXT_SIZE 5 |
408 | /* Number of chars we need to store generated CRC to make filename unique */ | ||
409 | #define CRC_LEN 5 | ||
406 | 410 | ||
407 | static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName, | 411 | static int udf_translate_to_linux(uint8_t *newName, int newLen, |
408 | int udfLen, uint8_t *fidName, | 412 | uint8_t *udfName, int udfLen, |
409 | int fidNameLen) | 413 | uint8_t *fidName, int fidNameLen) |
410 | { | 414 | { |
411 | int index, newIndex = 0, needsCRC = 0; | 415 | int index, newIndex = 0, needsCRC = 0; |
412 | int extIndex = 0, newExtIndex = 0, hasExt = 0; | 416 | int extIndex = 0, newExtIndex = 0, hasExt = 0; |
@@ -439,7 +443,7 @@ static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName, | |||
439 | newExtIndex = newIndex; | 443 | newExtIndex = newIndex; |
440 | } | 444 | } |
441 | } | 445 | } |
442 | if (newIndex < 256) | 446 | if (newIndex < newLen) |
443 | newName[newIndex++] = curr; | 447 | newName[newIndex++] = curr; |
444 | else | 448 | else |
445 | needsCRC = 1; | 449 | needsCRC = 1; |
@@ -467,13 +471,13 @@ static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName, | |||
467 | } | 471 | } |
468 | ext[localExtIndex++] = curr; | 472 | ext[localExtIndex++] = curr; |
469 | } | 473 | } |
470 | maxFilenameLen = 250 - localExtIndex; | 474 | maxFilenameLen = newLen - CRC_LEN - localExtIndex; |
471 | if (newIndex > maxFilenameLen) | 475 | if (newIndex > maxFilenameLen) |
472 | newIndex = maxFilenameLen; | 476 | newIndex = maxFilenameLen; |
473 | else | 477 | else |
474 | newIndex = newExtIndex; | 478 | newIndex = newExtIndex; |
475 | } else if (newIndex > 250) | 479 | } else if (newIndex > newLen - CRC_LEN) |
476 | newIndex = 250; | 480 | newIndex = newLen - CRC_LEN; |
477 | newName[newIndex++] = CRC_MARK; | 481 | newName[newIndex++] = CRC_MARK; |
478 | valueCRC = crc_itu_t(0, fidName, fidNameLen); | 482 | valueCRC = crc_itu_t(0, fidName, fidNameLen); |
479 | newName[newIndex++] = hex_asc_upper_hi(valueCRC >> 8); | 483 | newName[newIndex++] = hex_asc_upper_hi(valueCRC >> 8); |