aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2014-08-17 05:49:57 -0400
committerJan Kara <jack@suse.cz>2014-08-19 12:29:30 -0400
commit410dd3cf4c9b36f27ed4542ee18b1af5e68645a4 (patch)
treebe3af2da6fcc9bd0ef2518e6d344d726670b2df7 /fs
parent85cd083b498572fb9fa575cce3ed910c8ee84294 (diff)
isofs: Fix unbounded recursion when processing relocated directories
We did not check relocated directory in any way when processing Rock Ridge 'CL' tag. Thus a corrupted isofs image can possibly have a CL entry pointing to another CL entry leading to possibly unbounded recursion in kernel code and thus stack overflow or deadlocks (if there is a loop created from CL entries). Fix the problem by not allowing CL entry to point to a directory entry with CL entry (such use makes no good sense anyway) and by checking whether CL entry doesn't point to itself. CC: stable@vger.kernel.org Reported-by: Chris Evans <cevans@google.com> Signed-off-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'fs')
-rw-r--r--fs/isofs/inode.c15
-rw-r--r--fs/isofs/isofs.h23
-rw-r--r--fs/isofs/rock.c39
3 files changed, 55 insertions, 22 deletions
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index 4556ce1af5b0..5ddaf8625d3b 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -61,7 +61,7 @@ static void isofs_put_super(struct super_block *sb)
61 return; 61 return;
62} 62}
63 63
64static int isofs_read_inode(struct inode *); 64static int isofs_read_inode(struct inode *, int relocated);
65static int isofs_statfs (struct dentry *, struct kstatfs *); 65static int isofs_statfs (struct dentry *, struct kstatfs *);
66 66
67static struct kmem_cache *isofs_inode_cachep; 67static struct kmem_cache *isofs_inode_cachep;
@@ -1259,7 +1259,7 @@ out_toomany:
1259 goto out; 1259 goto out;
1260} 1260}
1261 1261
1262static int isofs_read_inode(struct inode *inode) 1262static int isofs_read_inode(struct inode *inode, int relocated)
1263{ 1263{
1264 struct super_block *sb = inode->i_sb; 1264 struct super_block *sb = inode->i_sb;
1265 struct isofs_sb_info *sbi = ISOFS_SB(sb); 1265 struct isofs_sb_info *sbi = ISOFS_SB(sb);
@@ -1404,7 +1404,7 @@ static int isofs_read_inode(struct inode *inode)
1404 */ 1404 */
1405 1405
1406 if (!high_sierra) { 1406 if (!high_sierra) {
1407 parse_rock_ridge_inode(de, inode); 1407 parse_rock_ridge_inode(de, inode, relocated);
1408 /* if we want uid/gid set, override the rock ridge setting */ 1408 /* if we want uid/gid set, override the rock ridge setting */
1409 if (sbi->s_uid_set) 1409 if (sbi->s_uid_set)
1410 inode->i_uid = sbi->s_uid; 1410 inode->i_uid = sbi->s_uid;
@@ -1483,9 +1483,10 @@ static int isofs_iget5_set(struct inode *ino, void *data)
1483 * offset that point to the underlying meta-data for the inode. The 1483 * offset that point to the underlying meta-data for the inode. The
1484 * code below is otherwise similar to the iget() code in 1484 * code below is otherwise similar to the iget() code in
1485 * include/linux/fs.h */ 1485 * include/linux/fs.h */
1486struct inode *isofs_iget(struct super_block *sb, 1486struct inode *__isofs_iget(struct super_block *sb,
1487 unsigned long block, 1487 unsigned long block,
1488 unsigned long offset) 1488 unsigned long offset,
1489 int relocated)
1489{ 1490{
1490 unsigned long hashval; 1491 unsigned long hashval;
1491 struct inode *inode; 1492 struct inode *inode;
@@ -1507,7 +1508,7 @@ struct inode *isofs_iget(struct super_block *sb,
1507 return ERR_PTR(-ENOMEM); 1508 return ERR_PTR(-ENOMEM);
1508 1509
1509 if (inode->i_state & I_NEW) { 1510 if (inode->i_state & I_NEW) {
1510 ret = isofs_read_inode(inode); 1511 ret = isofs_read_inode(inode, relocated);
1511 if (ret < 0) { 1512 if (ret < 0) {
1512 iget_failed(inode); 1513 iget_failed(inode);
1513 inode = ERR_PTR(ret); 1514 inode = ERR_PTR(ret);
diff --git a/fs/isofs/isofs.h b/fs/isofs/isofs.h
index 99167238518d..0ac4c1f73fbd 100644
--- a/fs/isofs/isofs.h
+++ b/fs/isofs/isofs.h
@@ -107,7 +107,7 @@ extern int iso_date(char *, int);
107 107
108struct inode; /* To make gcc happy */ 108struct inode; /* To make gcc happy */
109 109
110extern int parse_rock_ridge_inode(struct iso_directory_record *, struct inode *); 110extern int parse_rock_ridge_inode(struct iso_directory_record *, struct inode *, int relocated);
111extern int get_rock_ridge_filename(struct iso_directory_record *, char *, struct inode *); 111extern int get_rock_ridge_filename(struct iso_directory_record *, char *, struct inode *);
112extern int isofs_name_translate(struct iso_directory_record *, char *, struct inode *); 112extern int isofs_name_translate(struct iso_directory_record *, char *, struct inode *);
113 113
@@ -118,9 +118,24 @@ extern struct dentry *isofs_lookup(struct inode *, struct dentry *, unsigned int
118extern struct buffer_head *isofs_bread(struct inode *, sector_t); 118extern struct buffer_head *isofs_bread(struct inode *, sector_t);
119extern int isofs_get_blocks(struct inode *, sector_t, struct buffer_head **, unsigned long); 119extern int isofs_get_blocks(struct inode *, sector_t, struct buffer_head **, unsigned long);
120 120
121extern struct inode *isofs_iget(struct super_block *sb, 121struct inode *__isofs_iget(struct super_block *sb,
122 unsigned long block, 122 unsigned long block,
123 unsigned long offset); 123 unsigned long offset,
124 int relocated);
125
126static inline struct inode *isofs_iget(struct super_block *sb,
127 unsigned long block,
128 unsigned long offset)
129{
130 return __isofs_iget(sb, block, offset, 0);
131}
132
133static inline struct inode *isofs_iget_reloc(struct super_block *sb,
134 unsigned long block,
135 unsigned long offset)
136{
137 return __isofs_iget(sb, block, offset, 1);
138}
124 139
125/* Because the inode number is no longer relevant to finding the 140/* Because the inode number is no longer relevant to finding the
126 * underlying meta-data for an inode, we are free to choose a more 141 * underlying meta-data for an inode, we are free to choose a more
diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c
index c0bf42472e40..f488bbae541a 100644
--- a/fs/isofs/rock.c
+++ b/fs/isofs/rock.c
@@ -288,12 +288,16 @@ eio:
288 goto out; 288 goto out;
289} 289}
290 290
291#define RR_REGARD_XA 1
292#define RR_RELOC_DE 2
293
291static int 294static int
292parse_rock_ridge_inode_internal(struct iso_directory_record *de, 295parse_rock_ridge_inode_internal(struct iso_directory_record *de,
293 struct inode *inode, int regard_xa) 296 struct inode *inode, int flags)
294{ 297{
295 int symlink_len = 0; 298 int symlink_len = 0;
296 int cnt, sig; 299 int cnt, sig;
300 unsigned int reloc_block;
297 struct inode *reloc; 301 struct inode *reloc;
298 struct rock_ridge *rr; 302 struct rock_ridge *rr;
299 int rootflag; 303 int rootflag;
@@ -305,7 +309,7 @@ parse_rock_ridge_inode_internal(struct iso_directory_record *de,
305 309
306 init_rock_state(&rs, inode); 310 init_rock_state(&rs, inode);
307 setup_rock_ridge(de, inode, &rs); 311 setup_rock_ridge(de, inode, &rs);
308 if (regard_xa) { 312 if (flags & RR_REGARD_XA) {
309 rs.chr += 14; 313 rs.chr += 14;
310 rs.len -= 14; 314 rs.len -= 14;
311 if (rs.len < 0) 315 if (rs.len < 0)
@@ -485,12 +489,22 @@ repeat:
485 "relocated directory\n"); 489 "relocated directory\n");
486 goto out; 490 goto out;
487 case SIG('C', 'L'): 491 case SIG('C', 'L'):
488 ISOFS_I(inode)->i_first_extent = 492 if (flags & RR_RELOC_DE) {
489 isonum_733(rr->u.CL.location); 493 printk(KERN_ERR
490 reloc = 494 "ISOFS: Recursive directory relocation "
491 isofs_iget(inode->i_sb, 495 "is not supported\n");
492 ISOFS_I(inode)->i_first_extent, 496 goto eio;
493 0); 497 }
498 reloc_block = isonum_733(rr->u.CL.location);
499 if (reloc_block == ISOFS_I(inode)->i_iget5_block &&
500 ISOFS_I(inode)->i_iget5_offset == 0) {
501 printk(KERN_ERR
502 "ISOFS: Directory relocation points to "
503 "itself\n");
504 goto eio;
505 }
506 ISOFS_I(inode)->i_first_extent = reloc_block;
507 reloc = isofs_iget_reloc(inode->i_sb, reloc_block, 0);
494 if (IS_ERR(reloc)) { 508 if (IS_ERR(reloc)) {
495 ret = PTR_ERR(reloc); 509 ret = PTR_ERR(reloc);
496 goto out; 510 goto out;
@@ -637,9 +651,11 @@ static char *get_symlink_chunk(char *rpnt, struct rock_ridge *rr, char *plimit)
637 return rpnt; 651 return rpnt;
638} 652}
639 653
640int parse_rock_ridge_inode(struct iso_directory_record *de, struct inode *inode) 654int parse_rock_ridge_inode(struct iso_directory_record *de, struct inode *inode,
655 int relocated)
641{ 656{
642 int result = parse_rock_ridge_inode_internal(de, inode, 0); 657 int flags = relocated ? RR_RELOC_DE : 0;
658 int result = parse_rock_ridge_inode_internal(de, inode, flags);
643 659
644 /* 660 /*
645 * if rockridge flag was reset and we didn't look for attributes 661 * if rockridge flag was reset and we didn't look for attributes
@@ -647,7 +663,8 @@ int parse_rock_ridge_inode(struct iso_directory_record *de, struct inode *inode)
647 */ 663 */
648 if ((ISOFS_SB(inode->i_sb)->s_rock_offset == -1) 664 if ((ISOFS_SB(inode->i_sb)->s_rock_offset == -1)
649 && (ISOFS_SB(inode->i_sb)->s_rock == 2)) { 665 && (ISOFS_SB(inode->i_sb)->s_rock == 2)) {
650 result = parse_rock_ridge_inode_internal(de, inode, 14); 666 result = parse_rock_ridge_inode_internal(de, inode,
667 flags | RR_REGARD_XA);
651 } 668 }
652 return result; 669 return result;
653} 670}