CVE-2014-5471 (C, published 2014-09-01T01:55Z)
Description: value: Stack consumption vulnerability in 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 (uncontrolled recursion, and system crash or reboot) via a crafted iso9660 image with a CL entry referring to a directory entry that has a CL entry.
Severity: MEDIUM (CVSS v3 base score: )
Weakness: CWE-399 \u2014 Resource Management Errors
Weaknesses in this category are related to improper management of system resources.
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);