aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2014-12-18 16:37:50 -0500
committerJan Kara <jack@suse.cz>2014-12-19 08:12:08 -0500
commit0e5cc9a40ada6046e6bc3bdfcd0c0d7e4b706b14 (patch)
tree6a9a8bef5942cb6b18ae977fd1be24c59cebe71c /fs
parenta1d47b262952a45aae62bd49cfaf33dd76c11a2c (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.c3
-rw-r--r--fs/udf/namei.c3
-rw-r--r--fs/udf/symlink.c31
-rw-r--r--fs/udf/udfdecl.h3
-rw-r--r--fs/udf/unicode.c28
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
33static void udf_pc_to_char(struct super_block *sb, unsigned char *from, 33static 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
78static int udf_symlink_filler(struct file *file, struct page *page) 97static 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 */
214extern int udf_get_filename(struct super_block *, uint8_t *, uint8_t *, int); 214extern int udf_get_filename(struct super_block *, uint8_t *, int, uint8_t *,
215 int);
215extern int udf_put_filename(struct super_block *, const uint8_t *, uint8_t *, 216extern int udf_put_filename(struct super_block *, const uint8_t *, uint8_t *,
216 int); 217 int);
217extern int udf_build_ustr(struct ustr *, dstring *, int); 218extern 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
31static int udf_translate_to_linux(uint8_t *, uint8_t *, int, uint8_t *, int); 31static int udf_translate_to_linux(uint8_t *, int, uint8_t *, int, uint8_t *,
32 int);
32 33
33static int udf_char_to_ustr(struct ustr *dest, const uint8_t *src, int strlen) 34static 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
336int udf_get_filename(struct super_block *sb, uint8_t *sname, uint8_t *dname, 337int 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);
371out2: 373out2:
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
407static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName, 411static 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);