Commit 1a927faa55b967fdc6f8fcb2a8bc9870ee7c0d98

Authored by Jan Kara
Committed by Greg Kroah-Hartman
1 parent 9cc010cac4

udf: Check path length when reading symlink

commit 0e5cc9a40ada6046e6bc3bdfcd0c0d7e4b706b14 upstream.

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.

Reported-by: Carl Henrik Lunde <chlunde@ping.uio.no>
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

Showing 5 changed files with 48 additions and 20 deletions Side-by-side Diff

... ... @@ -167,7 +167,8 @@
167 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 172 if (!flen)
172 173 continue;
173 174  
... ... @@ -233,7 +233,8 @@
233 233 if (!lfi)
234 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 238 if (flen && udf_match(flen, fname, child->len, child->name))
238 239 goto out_ok;
239 240 }
... ... @@ -30,13 +30,16 @@
30 30 #include <linux/buffer_head.h>
31 31 #include "udf_i.h"
32 32  
33   -static void udf_pc_to_char(struct super_block *sb, unsigned char *from,
34   - int fromlen, unsigned char *to)
  33 +static int udf_pc_to_char(struct super_block *sb, unsigned char *from,
  34 + int fromlen, unsigned char *to, int tolen)
35 35 {
36 36 struct pathComponent *pc;
37 37 int elen = 0;
  38 + int comp_len;
38 39 unsigned char *p = to;
39 40  
  41 + /* Reserve one byte for terminating \0 */
  42 + tolen--;
40 43 while (elen < fromlen) {
41 44 pc = (struct pathComponent *)(from + elen);
42 45 switch (pc->componentType) {
43 46  
44 47  
45 48  
46 49  
47 50  
48 51  
49 52  
... ... @@ -49,22 +52,37 @@
49 52 break;
50 53 /* Fall through */
51 54 case 2:
  55 + if (tolen == 0)
  56 + return -ENAMETOOLONG;
52 57 p = to;
53 58 *p++ = '/';
  59 + tolen--;
54 60 break;
55 61 case 3:
  62 + if (tolen < 3)
  63 + return -ENAMETOOLONG;
56 64 memcpy(p, "../", 3);
57 65 p += 3;
  66 + tolen -= 3;
58 67 break;
59 68 case 4:
  69 + if (tolen < 2)
  70 + return -ENAMETOOLONG;
60 71 memcpy(p, "./", 2);
61 72 p += 2;
  73 + tolen -= 2;
62 74 /* that would be . - just ignore */
63 75 break;
64 76 case 5:
65   - p += udf_get_filename(sb, pc->componentIdent, p,
66   - pc->lengthComponentIdent);
  77 + comp_len = udf_get_filename(sb, pc->componentIdent,
  78 + pc->lengthComponentIdent,
  79 + p, tolen);
  80 + p += comp_len;
  81 + tolen -= comp_len;
  82 + if (tolen == 0)
  83 + return -ENAMETOOLONG;
67 84 *p++ = '/';
  85 + tolen--;
68 86 break;
69 87 }
70 88 elen += sizeof(struct pathComponent) + pc->lengthComponentIdent;
... ... @@ -73,6 +91,7 @@
73 91 p[-1] = '\0';
74 92 else
75 93 p[0] = '\0';
  94 + return 0;
76 95 }
77 96  
78 97 static int udf_symlink_filler(struct file *file, struct page *page)
79 98  
... ... @@ -100,8 +119,10 @@
100 119 symlink = bh->b_data;
101 120 }
102 121  
103   - udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p);
  122 + err = udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p, PAGE_SIZE);
104 123 brelse(bh);
  124 + if (err)
  125 + goto out_unlock_inode;
105 126  
106 127 up_read(&iinfo->i_data_sem);
107 128 SetPageUptodate(page);
... ... @@ -211,7 +211,8 @@
211 211 }
212 212  
213 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 216 extern int udf_put_filename(struct super_block *, const uint8_t *, uint8_t *,
216 217 int);
217 218 extern int udf_build_ustr(struct ustr *, dstring *, int);
... ... @@ -28,7 +28,8 @@
28 28  
29 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 34 static int udf_char_to_ustr(struct ustr *dest, const uint8_t *src, int strlen)
34 35 {
... ... @@ -333,8 +334,8 @@
333 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 flen)
  337 +int udf_get_filename(struct super_block *sb, uint8_t *sname, int slen,
  338 + uint8_t *dname, int dlen)
338 339 {
339 340 struct ustr *filename, *unifilename;
340 341 int len = 0;
... ... @@ -347,7 +348,7 @@
347 348 if (!unifilename)
348 349 goto out1;
349 350  
350   - if (udf_build_ustr_exact(unifilename, sname, flen))
  351 + if (udf_build_ustr_exact(unifilename, sname, slen))
351 352 goto out2;
352 353  
353 354 if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) {
... ... @@ -366,7 +367,8 @@
366 367 } else
367 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 372 unifilename->u_name, unifilename->u_len);
371 373 out2:
372 374 kfree(unifilename);
373 375  
... ... @@ -403,10 +405,12 @@
403 405 #define EXT_MARK '.'
404 406 #define CRC_MARK '#'
405 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,
408   - int udfLen, uint8_t *fidName,
409   - int fidNameLen)
  411 +static int udf_translate_to_linux(uint8_t *newName, int newLen,
  412 + uint8_t *udfName, int udfLen,
  413 + uint8_t *fidName, int fidNameLen)
410 414 {
411 415 int index, newIndex = 0, needsCRC = 0;
412 416 int extIndex = 0, newExtIndex = 0, hasExt = 0;
... ... @@ -439,7 +443,7 @@
439 443 newExtIndex = newIndex;
440 444 }
441 445 }
442   - if (newIndex < 256)
  446 + if (newIndex < newLen)
443 447 newName[newIndex++] = curr;
444 448 else
445 449 needsCRC = 1;
446 450  
... ... @@ -467,13 +471,13 @@
467 471 }
468 472 ext[localExtIndex++] = curr;
469 473 }
470   - maxFilenameLen = 250 - localExtIndex;
  474 + maxFilenameLen = newLen - CRC_LEN - localExtIndex;
471 475 if (newIndex > maxFilenameLen)
472 476 newIndex = maxFilenameLen;
473 477 else
474 478 newIndex = newExtIndex;
475   - } else if (newIndex > 250)
476   - newIndex = 250;
  479 + } else if (newIndex > newLen - CRC_LEN)
  480 + newIndex = newLen - CRC_LEN;
477 481 newName[newIndex++] = CRC_MARK;
478 482 valueCRC = crc_itu_t(0, fidName, fidNameLen);
479 483 newName[newIndex++] = hex_asc_upper_hi(valueCRC >> 8);