CVE-2014-9731 (C, published 2015-08-31T10:59Z)
Description: value: The UDF filesystem implementation in the Linux kernel before 3.18.2 does not ensure that space is available for storing a symlink target's name along with a trailing \0 character, which allows local users to obtain sensitive information via a crafted filesystem image, related to fs/udf/symlink.c and fs/udf/unicode.c.
Severity: LOW (CVSS v3 base score: )
Weakness: CWE-17 \u2014 DEPRECATED: Code
This entry has been deprecated.  It was originally used for organizing the Development View (CWE-699) and some other views, but it introduced unnecessary complexity and depth to the resulting tree.
Repository: https://github.com/torvalds/linux
Files changed:
fs/udf/dir.c
fs/udf/namei.c
fs/udf/symlink.c
fs/udf/udfdecl.h
fs/udf/unicode.c
Commit message: udf: Check path length when reading symlink

Symlink reading code does not check whether the resulting path fits into
the page provided by the generic code. This isn't as easy as just
checking the symlink size because of various encoding conversions we
perform on path. So we have to check whether there is still enough space
in the buffer on the fly.

CC: stable@vger.kernel.org
Reported-by: Carl Henrik Lunde <chlunde@ping.uio.no>
Signed-off-by: Jan Kara <jack@suse.cz>


Vulnerable code:
		flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi);
		flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi);
static void udf_pc_to_char(struct super_block *sb, unsigned char *from,
			   int fromlen, unsigned char *to)
			p += udf_get_filename(sb, pc->componentIdent, p,
					      pc->lengthComponentIdent);
	udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p);
extern int udf_get_filename(struct super_block *, uint8_t *, uint8_t *, int);
static int udf_translate_to_linux(uint8_t *, uint8_t *, int, uint8_t *, int);
int udf_get_filename(struct super_block *sb, uint8_t *sname, uint8_t *dname,
		     int flen)
	if (udf_build_ustr_exact(unifilename, sname, flen))
	len = udf_translate_to_linux(dname, filename->u_name, filename->u_len,
static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName,
				  int udfLen, uint8_t *fidName,
				  int fidNameLen)
			if (newIndex < 256)
			maxFilenameLen = 250 - localExtIndex;
		} else if (newIndex > 250)
			newIndex = 250;

Fixed code:
		flen = udf_get_filename(dir->i_sb, nameptr, lfi, fname,
					UDF_NAME_LEN);
		flen = udf_get_filename(dir->i_sb, nameptr, lfi, fname,
					UDF_NAME_LEN);
static int udf_pc_to_char(struct super_block *sb, unsigned char *from,
			  int fromlen, unsigned char *to, int tolen)
	int comp_len;
	/* Reserve one byte for terminating \0 */
	tolen--;
			if (tolen == 0)
				return -ENAMETOOLONG;
			tolen--;
			if (tolen < 3)
				return -ENAMETOOLONG;
			tolen -= 3;
			if (tolen < 2)
				return -ENAMETOOLONG;
			tolen -= 2;
			comp_len = udf_get_filename(sb, pc->componentIdent,
						    pc->lengthComponentIdent,
						    p, tolen);
			p += comp_len;
			tolen -= comp_len;
			if (tolen == 0)
				return -ENAMETOOLONG;
			tolen--;
	return 0;
	err = udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p, PAGE_SIZE);
	if (err)
		goto out_unlock_inode;
extern int udf_get_filename(struct super_block *, uint8_t *, int, uint8_t *,
			    int);
static int udf_translate_to_linux(uint8_t *, int, uint8_t *, int, uint8_t *,
				  int);
int udf_get_filename(struct super_block *sb, uint8_t *sname, int slen,
		     uint8_t *dname, int dlen)
	if (udf_build_ustr_exact(unifilename, sname, slen))
	len = udf_translate_to_linux(dname, dlen,
				     filename->u_name, filename->u_len,
/* Number of chars we need to store generated CRC to make filename unique */
#define CRC_LEN			5
static int udf_translate_to_linux(uint8_t *newName, int newLen,
				  uint8_t *udfName, int udfLen,
				  uint8_t *fidName, int fidNameLen)
			if (newIndex < newLen)
			maxFilenameLen = newLen - CRC_LEN - localExtIndex;
		} else if (newIndex > newLen - CRC_LEN)
			newIndex = newLen - CRC_LEN;