diff options
author | Jan Kara <jack@suse.cz> | 2014-08-17 05:49:57 -0400 |
---|---|---|
committer | Jan Kara <jack@suse.cz> | 2014-08-19 12:29:30 -0400 |
commit | 410dd3cf4c9b36f27ed4542ee18b1af5e68645a4 (patch) | |
tree | be3af2da6fcc9bd0ef2518e6d344d726670b2df7 /fs | |
parent | 85cd083b498572fb9fa575cce3ed910c8ee84294 (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.c | 15 | ||||
-rw-r--r-- | fs/isofs/isofs.h | 23 | ||||
-rw-r--r-- | fs/isofs/rock.c | 39 |
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 | ||
64 | static int isofs_read_inode(struct inode *); | 64 | static int isofs_read_inode(struct inode *, int relocated); |
65 | static int isofs_statfs (struct dentry *, struct kstatfs *); | 65 | static int isofs_statfs (struct dentry *, struct kstatfs *); |
66 | 66 | ||
67 | static struct kmem_cache *isofs_inode_cachep; | 67 | static struct kmem_cache *isofs_inode_cachep; |
@@ -1259,7 +1259,7 @@ out_toomany: | |||
1259 | goto out; | 1259 | goto out; |
1260 | } | 1260 | } |
1261 | 1261 | ||
1262 | static int isofs_read_inode(struct inode *inode) | 1262 | static 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 */ |
1486 | struct inode *isofs_iget(struct super_block *sb, | 1486 | struct 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 | ||
108 | struct inode; /* To make gcc happy */ | 108 | struct inode; /* To make gcc happy */ |
109 | 109 | ||
110 | extern int parse_rock_ridge_inode(struct iso_directory_record *, struct inode *); | 110 | extern int parse_rock_ridge_inode(struct iso_directory_record *, struct inode *, int relocated); |
111 | extern int get_rock_ridge_filename(struct iso_directory_record *, char *, struct inode *); | 111 | extern int get_rock_ridge_filename(struct iso_directory_record *, char *, struct inode *); |
112 | extern int isofs_name_translate(struct iso_directory_record *, char *, struct inode *); | 112 | extern 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 | |||
118 | extern struct buffer_head *isofs_bread(struct inode *, sector_t); | 118 | extern struct buffer_head *isofs_bread(struct inode *, sector_t); |
119 | extern int isofs_get_blocks(struct inode *, sector_t, struct buffer_head **, unsigned long); | 119 | extern int isofs_get_blocks(struct inode *, sector_t, struct buffer_head **, unsigned long); |
120 | 120 | ||
121 | extern struct inode *isofs_iget(struct super_block *sb, | 121 | struct 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 | |||
126 | static 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 | |||
133 | static 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 | |||
291 | static int | 294 | static int |
292 | parse_rock_ridge_inode_internal(struct iso_directory_record *de, | 295 | parse_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 | ||
640 | int parse_rock_ridge_inode(struct iso_directory_record *de, struct inode *inode) | 654 | int 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 | } |