Skip to content

Commit

Permalink
udf: Check path length when reading symlink
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Jan Kara committed Dec 19, 2014
1 parent a1d47b2 commit 0e5cc9a
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 20 deletions.
3 changes: 2 additions & 1 deletion fs/udf/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,8 @@ static int udf_readdir(struct file *file, struct dir_context *ctx)
continue;
}

flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi);
flen = udf_get_filename(dir->i_sb, nameptr, lfi, fname,
UDF_NAME_LEN);
if (!flen)
continue;

Expand Down
3 changes: 2 additions & 1 deletion fs/udf/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,8 @@ static struct fileIdentDesc *udf_find_entry(struct inode *dir,
if (!lfi)
continue;

flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi);
flen = udf_get_filename(dir->i_sb, nameptr, lfi, fname,
UDF_NAME_LEN);
if (flen && udf_match(flen, fname, child->len, child->name))
goto out_ok;
}
Expand Down
31 changes: 26 additions & 5 deletions fs/udf/symlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,16 @@
#include <linux/buffer_head.h>
#include "udf_i.h"

static void udf_pc_to_char(struct super_block *sb, unsigned char *from,
int fromlen, unsigned char *to)
static int udf_pc_to_char(struct super_block *sb, unsigned char *from,
int fromlen, unsigned char *to, int tolen)
{
struct pathComponent *pc;
int elen = 0;
int comp_len;
unsigned char *p = to;

/* Reserve one byte for terminating \0 */
tolen--;
while (elen < fromlen) {
pc = (struct pathComponent *)(from + elen);
switch (pc->componentType) {
Expand All @@ -49,22 +52,37 @@ static void udf_pc_to_char(struct super_block *sb, unsigned char *from,
break;
/* Fall through */
case 2:
if (tolen == 0)
return -ENAMETOOLONG;
p = to;
*p++ = '/';
tolen--;
break;
case 3:
if (tolen < 3)
return -ENAMETOOLONG;
memcpy(p, "../", 3);
p += 3;
tolen -= 3;
break;
case 4:
if (tolen < 2)
return -ENAMETOOLONG;
memcpy(p, "./", 2);
p += 2;
tolen -= 2;
/* that would be . - just ignore */
break;
case 5:
p += udf_get_filename(sb, pc->componentIdent, p,
pc->lengthComponentIdent);
comp_len = udf_get_filename(sb, pc->componentIdent,
pc->lengthComponentIdent,
p, tolen);
p += comp_len;
tolen -= comp_len;
if (tolen == 0)
return -ENAMETOOLONG;
*p++ = '/';
tolen--;
break;
}
elen += sizeof(struct pathComponent) + pc->lengthComponentIdent;
Expand All @@ -73,6 +91,7 @@ static void udf_pc_to_char(struct super_block *sb, unsigned char *from,
p[-1] = '\0';
else
p[0] = '\0';
return 0;
}

static int udf_symlink_filler(struct file *file, struct page *page)
Expand Down Expand Up @@ -108,8 +127,10 @@ static int udf_symlink_filler(struct file *file, struct page *page)
symlink = bh->b_data;
}

udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p);
err = udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p, PAGE_SIZE);
brelse(bh);
if (err)
goto out_unlock_inode;

up_read(&iinfo->i_data_sem);
SetPageUptodate(page);
Expand Down
3 changes: 2 additions & 1 deletion fs/udf/udfdecl.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,8 @@ udf_get_lb_pblock(struct super_block *sb, struct kernel_lb_addr *loc,
}

/* unicode.c */
extern int udf_get_filename(struct super_block *, uint8_t *, uint8_t *, int);
extern int udf_get_filename(struct super_block *, uint8_t *, int, uint8_t *,
int);
extern int udf_put_filename(struct super_block *, const uint8_t *, uint8_t *,
int);
extern int udf_build_ustr(struct ustr *, dstring *, int);
Expand Down
28 changes: 16 additions & 12 deletions fs/udf/unicode.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@

#include "udf_sb.h"

static int udf_translate_to_linux(uint8_t *, uint8_t *, int, uint8_t *, int);
static int udf_translate_to_linux(uint8_t *, int, uint8_t *, int, uint8_t *,
int);

static int udf_char_to_ustr(struct ustr *dest, const uint8_t *src, int strlen)
{
Expand Down Expand Up @@ -333,8 +334,8 @@ static int udf_NLStoCS0(struct nls_table *nls, dstring *ocu, struct ustr *uni,
return u_len + 1;
}

int udf_get_filename(struct super_block *sb, uint8_t *sname, uint8_t *dname,
int flen)
int udf_get_filename(struct super_block *sb, uint8_t *sname, int slen,
uint8_t *dname, int dlen)
{
struct ustr *filename, *unifilename;
int len = 0;
Expand All @@ -347,7 +348,7 @@ int udf_get_filename(struct super_block *sb, uint8_t *sname, uint8_t *dname,
if (!unifilename)
goto out1;

if (udf_build_ustr_exact(unifilename, sname, flen))
if (udf_build_ustr_exact(unifilename, sname, slen))
goto out2;

if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) {
Expand All @@ -366,7 +367,8 @@ int udf_get_filename(struct super_block *sb, uint8_t *sname, uint8_t *dname,
} else
goto out2;

len = udf_translate_to_linux(dname, filename->u_name, filename->u_len,
len = udf_translate_to_linux(dname, dlen,
filename->u_name, filename->u_len,
unifilename->u_name, unifilename->u_len);
out2:
kfree(unifilename);
Expand Down Expand Up @@ -403,10 +405,12 @@ int udf_put_filename(struct super_block *sb, const uint8_t *sname,
#define EXT_MARK '.'
#define CRC_MARK '#'
#define EXT_SIZE 5
/* 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, uint8_t *udfName,
int udfLen, uint8_t *fidName,
int fidNameLen)
static int udf_translate_to_linux(uint8_t *newName, int newLen,
uint8_t *udfName, int udfLen,
uint8_t *fidName, int fidNameLen)
{
int index, newIndex = 0, needsCRC = 0;
int extIndex = 0, newExtIndex = 0, hasExt = 0;
Expand Down Expand Up @@ -439,7 +443,7 @@ static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName,
newExtIndex = newIndex;
}
}
if (newIndex < 256)
if (newIndex < newLen)
newName[newIndex++] = curr;
else
needsCRC = 1;
Expand Down Expand Up @@ -467,13 +471,13 @@ static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName,
}
ext[localExtIndex++] = curr;
}
maxFilenameLen = 250 - localExtIndex;
maxFilenameLen = newLen - CRC_LEN - localExtIndex;
if (newIndex > maxFilenameLen)
newIndex = maxFilenameLen;
else
newIndex = newExtIndex;
} else if (newIndex > 250)
newIndex = 250;
} else if (newIndex > newLen - CRC_LEN)
newIndex = newLen - CRC_LEN;
newName[newIndex++] = CRC_MARK;
valueCRC = crc_itu_t(0, fidName, fidNameLen);
newName[newIndex++] = hex_asc_upper_hi(valueCRC >> 8);
Expand Down

0 comments on commit 0e5cc9a

Please sign in to comment.