CVE-2014-5472 (C, published 2014-09-01T01:55Z)
Description: value: The parse_rock_ridge_inode_internal function in fs/isofs/rock.c in the Linux kernel through 3.16.1 allows local users to cause a denial of service (unkillable mount process) via a crafted iso9660 image with a self-referential CL entry.
Severity: MEDIUM (CVSS v3 base score: )
Weakness: CWE-20 \u2014 Improper Input Validation
The product receives input or data, but it does
        not validate or incorrectly validates that the input has the
        properties that are required to process the data safely and
        correctly.
Repository: https://github.com/torvalds/linux
Files changed:
fs/isofs/inode.c
fs/isofs/isofs.h
fs/isofs/rock.c
Commit message: 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>


Vulnerable code:
static int isofs_read_inode(struct inode *);
static int isofs_read_inode(struct inode *inode)
		parse_rock_ridge_inode(de, inode);
struct inode *isofs_iget(struct super_block *sb,
			 unsigned long block,
			 unsigned long offset)
		ret = isofs_read_inode(inode);
extern int parse_rock_ridge_inode(struct iso_directory_record *, struct inode *);
extern struct inode *isofs_iget(struct super_block *sb,
                                unsigned long block,
                                unsigned long offset);
				struct inode *inode, int regard_xa)
	if (regard_xa) {
			ISOFS_I(inode)->i_first_extent =
			    isonum_733(rr->u.CL.location);
			reloc =
			    isofs_iget(inode->i_sb,
				       ISOFS_I(inode)->i_first_extent,
				       0);
int parse_rock_ridge_inode(struct iso_directory_record *de, struct inode *inode)
	int result = parse_rock_ridge_inode_internal(de, inode, 0);
		result = parse_rock_ridge_inode_internal(de, inode, 14);

Fixed code:
static int isofs_read_inode(struct inode *, int relocated);
static int isofs_read_inode(struct inode *inode, int relocated)
		parse_rock_ridge_inode(de, inode, relocated);
struct inode *__isofs_iget(struct super_block *sb,
			   unsigned long block,
			   unsigned long offset,
			   int relocated)
		ret = isofs_read_inode(inode, relocated);
extern int parse_rock_ridge_inode(struct iso_directory_record *, struct inode *, int relocated);
struct inode *__isofs_iget(struct super_block *sb,
			   unsigned long block,
			   unsigned long offset,
			   int relocated);

static inline struct inode *isofs_iget(struct super_block *sb,
				       unsigned long block,
				       unsigned long offset)
{
	return __isofs_iget(sb, block, offset, 0);
}

static inline struct inode *isofs_iget_reloc(struct super_block *sb,
					     unsigned long block,
					     unsigned long offset)
{
	return __isofs_iget(sb, block, offset, 1);
}
#define RR_REGARD_XA 1
#define RR_RELOC_DE 2

				struct inode *inode, int flags)
	unsigned int reloc_block;
	if (flags & RR_REGARD_XA) {
			if (flags & RR_RELOC_DE) {
				printk(KERN_ERR
				       "ISOFS: Recursive directory relocation "
				       "is not supported\n");
				goto eio;
			}
			reloc_block = isonum_733(rr->u.CL.location);
			if (reloc_block == ISOFS_I(inode)->i_iget5_block &&
			    ISOFS_I(inode)->i_iget5_offset == 0) {
				printk(KERN_ERR
				       "ISOFS: Directory relocation points to "
				       "itself\n");
				goto eio;
			}
			ISOFS_I(inode)->i_first_extent = reloc_block;
			reloc = isofs_iget_reloc(inode->i_sb, reloc_block, 0);
int parse_rock_ridge_inode(struct iso_directory_record *de, struct inode *inode,
			   int relocated)
	int flags = relocated ? RR_RELOC_DE : 0;
	int result = parse_rock_ridge_inode_internal(de, inode, flags);
		result = parse_rock_ridge_inode_internal(de, inode,
							 flags | RR_REGARD_XA);