Commit b9d4a35f0a5dd25b85462741a8fb539b355ea95c

Authored by Linus Torvalds

Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs

Pull UDF & isofs fixes from Jan Kara:
 "A couple of UDF fixes of handling of corrupted media and one iso9660
  fix of the same"

* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs:
  udf: Reduce repeated dereferences
  udf: Check component length before reading it
  udf: Check path length when reading symlink
  udf: Verify symlink size before loading it
  udf: Verify i_size when loading inode
  isofs: Fix unchecked printing of ER records

Showing 7 changed files Side-by-side Diff

... ... @@ -362,6 +362,9 @@
362 362 rs.cont_size = isonum_733(rr->u.CE.size);
363 363 break;
364 364 case SIG('E', 'R'):
  365 + /* Invalid length of ER tag id? */
  366 + if (rr->u.ER.len_id + offsetof(struct rock_ridge, u.ER.data) > rr->len)
  367 + goto out;
365 368 ISOFS_SB(inode->i_sb)->s_rock = 1;
366 369 printk(KERN_DEBUG "ISO 9660 Extensions: ");
367 370 {
... ... @@ -57,6 +57,7 @@
57 57 sector_t offset;
58 58 int i, num, ret = 0;
59 59 struct extent_position epos = { NULL, 0, {0, 0} };
  60 + struct super_block *sb = dir->i_sb;
60 61  
61 62 if (ctx->pos == 0) {
62 63 if (!dir_emit_dot(file, ctx))
63 64  
64 65  
... ... @@ -76,16 +77,16 @@
76 77 if (nf_pos == 0)
77 78 nf_pos = udf_ext0_offset(dir);
78 79  
79   - fibh.soffset = fibh.eoffset = nf_pos & (dir->i_sb->s_blocksize - 1);
  80 + fibh.soffset = fibh.eoffset = nf_pos & (sb->s_blocksize - 1);
80 81 if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
81   - if (inode_bmap(dir, nf_pos >> dir->i_sb->s_blocksize_bits,
  82 + if (inode_bmap(dir, nf_pos >> sb->s_blocksize_bits,
82 83 &epos, &eloc, &elen, &offset)
83 84 != (EXT_RECORDED_ALLOCATED >> 30)) {
84 85 ret = -ENOENT;
85 86 goto out;
86 87 }
87   - block = udf_get_lb_pblock(dir->i_sb, &eloc, offset);
88   - if ((++offset << dir->i_sb->s_blocksize_bits) < elen) {
  88 + block = udf_get_lb_pblock(sb, &eloc, offset);
  89 + if ((++offset << sb->s_blocksize_bits) < elen) {
89 90 if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
90 91 epos.offset -= sizeof(struct short_ad);
91 92 else if (iinfo->i_alloc_type ==
92 93  
93 94  
... ... @@ -95,18 +96,18 @@
95 96 offset = 0;
96 97 }
97 98  
98   - if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block))) {
  99 + if (!(fibh.sbh = fibh.ebh = udf_tread(sb, block))) {
99 100 ret = -EIO;
100 101 goto out;
101 102 }
102 103  
103   - if (!(offset & ((16 >> (dir->i_sb->s_blocksize_bits - 9)) - 1))) {
104   - i = 16 >> (dir->i_sb->s_blocksize_bits - 9);
105   - if (i + offset > (elen >> dir->i_sb->s_blocksize_bits))
106   - i = (elen >> dir->i_sb->s_blocksize_bits) - offset;
  104 + if (!(offset & ((16 >> (sb->s_blocksize_bits - 9)) - 1))) {
  105 + i = 16 >> (sb->s_blocksize_bits - 9);
  106 + if (i + offset > (elen >> sb->s_blocksize_bits))
  107 + i = (elen >> sb->s_blocksize_bits) - offset;
107 108 for (num = 0; i > 0; i--) {
108   - block = udf_get_lb_pblock(dir->i_sb, &eloc, offset + i);
109   - tmp = udf_tgetblk(dir->i_sb, block);
  109 + block = udf_get_lb_pblock(sb, &eloc, offset + i);
  110 + tmp = udf_tgetblk(sb, block);
110 111 if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))
111 112 bha[num++] = tmp;
112 113 else
113 114  
... ... @@ -152,12 +153,12 @@
152 153 }
153 154  
154 155 if ((cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) != 0) {
155   - if (!UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE))
  156 + if (!UDF_QUERY_FLAG(sb, UDF_FLAG_UNDELETE))
156 157 continue;
157 158 }
158 159  
159 160 if ((cfi.fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0) {
160   - if (!UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE))
  161 + if (!UDF_QUERY_FLAG(sb, UDF_FLAG_UNHIDE))
161 162 continue;
162 163 }
163 164  
164 165  
... ... @@ -167,12 +168,12 @@
167 168 continue;
168 169 }
169 170  
170   - flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi);
  171 + flen = udf_get_filename(sb, nameptr, lfi, fname, UDF_NAME_LEN);
171 172 if (!flen)
172 173 continue;
173 174  
174 175 tloc = lelb_to_cpu(cfi.icb.extLocation);
175   - iblock = udf_get_lb_pblock(dir->i_sb, &tloc, 0);
  176 + iblock = udf_get_lb_pblock(sb, &tloc, 0);
176 177 if (!dir_emit(ctx, fname, flen, iblock, DT_UNKNOWN))
177 178 goto out;
178 179 } /* end while */
... ... @@ -1489,6 +1489,20 @@
1489 1489 }
1490 1490 inode->i_generation = iinfo->i_unique;
1491 1491  
  1492 + /* Sanity checks for files in ICB so that we don't get confused later */
  1493 + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
  1494 + /*
  1495 + * For file in ICB data is stored in allocation descriptor
  1496 + * so sizes should match
  1497 + */
  1498 + if (iinfo->i_lenAlloc != inode->i_size)
  1499 + goto out;
  1500 + /* File in ICB has to fit in there... */
  1501 + if (inode->i_size > inode->i_sb->s_blocksize -
  1502 + udf_file_entry_alloc_offset(inode))
  1503 + goto out;
  1504 + }
  1505 +
1492 1506 switch (fe->icbTag.fileType) {
1493 1507 case ICBTAG_FILE_TYPE_DIRECTORY:
1494 1508 inode->i_op = &udf_dir_inode_operations;
... ... @@ -159,18 +159,19 @@
159 159 struct udf_inode_info *dinfo = UDF_I(dir);
160 160 int isdotdot = child->len == 2 &&
161 161 child->name[0] == '.' && child->name[1] == '.';
  162 + struct super_block *sb = dir->i_sb;
162 163  
163 164 size = udf_ext0_offset(dir) + dir->i_size;
164 165 f_pos = udf_ext0_offset(dir);
165 166  
166 167 fibh->sbh = fibh->ebh = NULL;
167   - fibh->soffset = fibh->eoffset = f_pos & (dir->i_sb->s_blocksize - 1);
  168 + fibh->soffset = fibh->eoffset = f_pos & (sb->s_blocksize - 1);
168 169 if (dinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
169   - if (inode_bmap(dir, f_pos >> dir->i_sb->s_blocksize_bits, &epos,
  170 + if (inode_bmap(dir, f_pos >> sb->s_blocksize_bits, &epos,
170 171 &eloc, &elen, &offset) != (EXT_RECORDED_ALLOCATED >> 30))
171 172 goto out_err;
172   - block = udf_get_lb_pblock(dir->i_sb, &eloc, offset);
173   - if ((++offset << dir->i_sb->s_blocksize_bits) < elen) {
  173 + block = udf_get_lb_pblock(sb, &eloc, offset);
  174 + if ((++offset << sb->s_blocksize_bits) < elen) {
174 175 if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
175 176 epos.offset -= sizeof(struct short_ad);
176 177 else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
... ... @@ -178,7 +179,7 @@
178 179 } else
179 180 offset = 0;
180 181  
181   - fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block);
  182 + fibh->sbh = fibh->ebh = udf_tread(sb, block);
182 183 if (!fibh->sbh)
183 184 goto out_err;
184 185 }
185 186  
... ... @@ -217,12 +218,12 @@
217 218 }
218 219  
219 220 if ((cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0) {
220   - if (!UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE))
  221 + if (!UDF_QUERY_FLAG(sb, UDF_FLAG_UNDELETE))
221 222 continue;
222 223 }
223 224  
224 225 if ((cfi->fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0) {
225   - if (!UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE))
  226 + if (!UDF_QUERY_FLAG(sb, UDF_FLAG_UNHIDE))
226 227 continue;
227 228 }
228 229  
... ... @@ -233,7 +234,7 @@
233 234 if (!lfi)
234 235 continue;
235 236  
236   - flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi);
  237 + flen = udf_get_filename(sb, nameptr, lfi, fname, UDF_NAME_LEN);
237 238 if (flen && udf_match(flen, fname, child->len, child->name))
238 239 goto out_ok;
239 240 }
... ... @@ -30,49 +30,73 @@
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);
  45 + elen += sizeof(struct pathComponent);
42 46 switch (pc->componentType) {
43 47 case 1:
44 48 /*
45 49 * Symlink points to some place which should be agreed
46 50 * upon between originator and receiver of the media. Ignore.
47 51 */
48   - if (pc->lengthComponentIdent > 0)
  52 + if (pc->lengthComponentIdent > 0) {
  53 + elen += pc->lengthComponentIdent;
49 54 break;
  55 + }
50 56 /* Fall through */
51 57 case 2:
  58 + if (tolen == 0)
  59 + return -ENAMETOOLONG;
52 60 p = to;
53 61 *p++ = '/';
  62 + tolen--;
54 63 break;
55 64 case 3:
  65 + if (tolen < 3)
  66 + return -ENAMETOOLONG;
56 67 memcpy(p, "../", 3);
57 68 p += 3;
  69 + tolen -= 3;
58 70 break;
59 71 case 4:
  72 + if (tolen < 2)
  73 + return -ENAMETOOLONG;
60 74 memcpy(p, "./", 2);
61 75 p += 2;
  76 + tolen -= 2;
62 77 /* that would be . - just ignore */
63 78 break;
64 79 case 5:
65   - p += udf_get_filename(sb, pc->componentIdent, p,
66   - pc->lengthComponentIdent);
  80 + elen += pc->lengthComponentIdent;
  81 + if (elen > fromlen)
  82 + return -EIO;
  83 + comp_len = udf_get_filename(sb, pc->componentIdent,
  84 + pc->lengthComponentIdent,
  85 + p, tolen);
  86 + p += comp_len;
  87 + tolen -= comp_len;
  88 + if (tolen == 0)
  89 + return -ENAMETOOLONG;
67 90 *p++ = '/';
  91 + tolen--;
68 92 break;
69 93 }
70   - elen += sizeof(struct pathComponent) + pc->lengthComponentIdent;
71 94 }
72 95 if (p > to + 1)
73 96 p[-1] = '\0';
74 97 else
75 98 p[0] = '\0';
  99 + return 0;
76 100 }
77 101  
78 102 static int udf_symlink_filler(struct file *file, struct page *page)
79 103  
... ... @@ -80,11 +104,17 @@
80 104 struct inode *inode = page->mapping->host;
81 105 struct buffer_head *bh = NULL;
82 106 unsigned char *symlink;
83   - int err = -EIO;
  107 + int err;
84 108 unsigned char *p = kmap(page);
85 109 struct udf_inode_info *iinfo;
86 110 uint32_t pos;
87 111  
  112 + /* We don't support symlinks longer than one block */
  113 + if (inode->i_size > inode->i_sb->s_blocksize) {
  114 + err = -ENAMETOOLONG;
  115 + goto out_unmap;
  116 + }
  117 +
88 118 iinfo = UDF_I(inode);
89 119 pos = udf_block_map(inode, 0);
90 120  
91 121  
92 122  
... ... @@ -94,14 +124,18 @@
94 124 } else {
95 125 bh = sb_bread(inode->i_sb, pos);
96 126  
97   - if (!bh)
98   - goto out;
  127 + if (!bh) {
  128 + err = -EIO;
  129 + goto out_unlock_inode;
  130 + }
99 131  
100 132 symlink = bh->b_data;
101 133 }
102 134  
103   - udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p);
  135 + err = udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p, PAGE_SIZE);
104 136 brelse(bh);
  137 + if (err)
  138 + goto out_unlock_inode;
105 139  
106 140 up_read(&iinfo->i_data_sem);
107 141 SetPageUptodate(page);
108 142  
... ... @@ -109,9 +143,10 @@
109 143 unlock_page(page);
110 144 return 0;
111 145  
112   -out:
  146 +out_unlock_inode:
113 147 up_read(&iinfo->i_data_sem);
114 148 SetPageError(page);
  149 +out_unmap:
115 150 kunmap(page);
116 151 unlock_page(page);
117 152 return err;
... ... @@ -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);