Skip to content

Commit 410dd3c

Browse files
committed
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>
1 parent 85cd083 commit 410dd3c

File tree

3 files changed

+55
-22
lines changed

3 files changed

+55
-22
lines changed

Diff for: fs/isofs/inode.c

+8-7
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ static void isofs_put_super(struct super_block *sb)
6161
return;
6262
}
6363

64-
static int isofs_read_inode(struct inode *);
64+
static int isofs_read_inode(struct inode *, int relocated);
6565
static int isofs_statfs (struct dentry *, struct kstatfs *);
6666

6767
static struct kmem_cache *isofs_inode_cachep;
@@ -1259,7 +1259,7 @@ static int isofs_read_level3_size(struct inode *inode)
12591259
goto out;
12601260
}
12611261

1262-
static int isofs_read_inode(struct inode *inode)
1262+
static int isofs_read_inode(struct inode *inode, int relocated)
12631263
{
12641264
struct super_block *sb = inode->i_sb;
12651265
struct isofs_sb_info *sbi = ISOFS_SB(sb);
@@ -1404,7 +1404,7 @@ static int isofs_read_inode(struct inode *inode)
14041404
*/
14051405

14061406
if (!high_sierra) {
1407-
parse_rock_ridge_inode(de, inode);
1407+
parse_rock_ridge_inode(de, inode, relocated);
14081408
/* if we want uid/gid set, override the rock ridge setting */
14091409
if (sbi->s_uid_set)
14101410
inode->i_uid = sbi->s_uid;
@@ -1483,9 +1483,10 @@ static int isofs_iget5_set(struct inode *ino, void *data)
14831483
* offset that point to the underlying meta-data for the inode. The
14841484
* code below is otherwise similar to the iget() code in
14851485
* include/linux/fs.h */
1486-
struct inode *isofs_iget(struct super_block *sb,
1487-
unsigned long block,
1488-
unsigned long offset)
1486+
struct inode *__isofs_iget(struct super_block *sb,
1487+
unsigned long block,
1488+
unsigned long offset,
1489+
int relocated)
14891490
{
14901491
unsigned long hashval;
14911492
struct inode *inode;
@@ -1507,7 +1508,7 @@ struct inode *isofs_iget(struct super_block *sb,
15071508
return ERR_PTR(-ENOMEM);
15081509

15091510
if (inode->i_state & I_NEW) {
1510-
ret = isofs_read_inode(inode);
1511+
ret = isofs_read_inode(inode, relocated);
15111512
if (ret < 0) {
15121513
iget_failed(inode);
15131514
inode = ERR_PTR(ret);

Diff for: fs/isofs/isofs.h

+19-4
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ extern int iso_date(char *, int);
107107

108108
struct inode; /* To make gcc happy */
109109

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);
111111
extern int get_rock_ridge_filename(struct iso_directory_record *, char *, struct inode *);
112112
extern int isofs_name_translate(struct iso_directory_record *, char *, struct inode *);
113113

@@ -118,9 +118,24 @@ extern struct dentry *isofs_lookup(struct inode *, struct dentry *, unsigned int
118118
extern struct buffer_head *isofs_bread(struct inode *, sector_t);
119119
extern int isofs_get_blocks(struct inode *, sector_t, struct buffer_head **, unsigned long);
120120

121-
extern struct inode *isofs_iget(struct super_block *sb,
122-
unsigned long block,
123-
unsigned long offset);
121+
struct inode *__isofs_iget(struct super_block *sb,
122+
unsigned long block,
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+
}
124139

125140
/* Because the inode number is no longer relevant to finding the
126141
* underlying meta-data for an inode, we are free to choose a more

Diff for: fs/isofs/rock.c

+28-11
Original file line numberDiff line numberDiff line change
@@ -288,12 +288,16 @@ int get_rock_ridge_filename(struct iso_directory_record *de,
288288
goto out;
289289
}
290290

291+
#define RR_REGARD_XA 1
292+
#define RR_RELOC_DE 2
293+
291294
static int
292295
parse_rock_ridge_inode_internal(struct iso_directory_record *de,
293-
struct inode *inode, int regard_xa)
296+
struct inode *inode, int flags)
294297
{
295298
int symlink_len = 0;
296299
int cnt, sig;
300+
unsigned int reloc_block;
297301
struct inode *reloc;
298302
struct rock_ridge *rr;
299303
int rootflag;
@@ -305,7 +309,7 @@ parse_rock_ridge_inode_internal(struct iso_directory_record *de,
305309

306310
init_rock_state(&rs, inode);
307311
setup_rock_ridge(de, inode, &rs);
308-
if (regard_xa) {
312+
if (flags & RR_REGARD_XA) {
309313
rs.chr += 14;
310314
rs.len -= 14;
311315
if (rs.len < 0)
@@ -485,12 +489,22 @@ parse_rock_ridge_inode_internal(struct iso_directory_record *de,
485489
"relocated directory\n");
486490
goto out;
487491
case SIG('C', 'L'):
488-
ISOFS_I(inode)->i_first_extent =
489-
isonum_733(rr->u.CL.location);
490-
reloc =
491-
isofs_iget(inode->i_sb,
492-
ISOFS_I(inode)->i_first_extent,
493-
0);
492+
if (flags & RR_RELOC_DE) {
493+
printk(KERN_ERR
494+
"ISOFS: Recursive directory relocation "
495+
"is not supported\n");
496+
goto eio;
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);
494508
if (IS_ERR(reloc)) {
495509
ret = PTR_ERR(reloc);
496510
goto out;
@@ -637,17 +651,20 @@ static char *get_symlink_chunk(char *rpnt, struct rock_ridge *rr, char *plimit)
637651
return rpnt;
638652
}
639653

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)
641656
{
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);
643659

644660
/*
645661
* if rockridge flag was reset and we didn't look for attributes
646662
* behind eventual XA attributes, have a look there
647663
*/
648664
if ((ISOFS_SB(inode->i_sb)->s_rock_offset == -1)
649665
&& (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);
651668
}
652669
return result;
653670
}

0 commit comments

Comments
 (0)