3.16.7-ckt4 -stable review patch.  If anyone has any objections, please let me 
know.

------------------

From: Jan Kara <j...@suse.cz>

commit 0e5cc9a40ada6046e6bc3bdfcd0c0d7e4b706b14 upstream.

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.

Reported-by: Carl Henrik Lunde <chlu...@ping.uio.no>
Signed-off-by: Jan Kara <j...@suse.cz>
Signed-off-by: Luis Henriques <luis.henriq...@canonical.com>
---
 fs/udf/dir.c     |  3 ++-
 fs/udf/namei.c   |  3 ++-
 fs/udf/symlink.c | 31 ++++++++++++++++++++++++++-----
 fs/udf/udfdecl.h |  3 ++-
 fs/udf/unicode.c | 28 ++++++++++++++++------------
 5 files changed, 48 insertions(+), 20 deletions(-)

diff --git a/fs/udf/dir.c b/fs/udf/dir.c
index a012c51caffd..a7690b46ce0a 100644
--- a/fs/udf/dir.c
+++ b/fs/udf/dir.c
@@ -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;
 
diff --git a/fs/udf/namei.c b/fs/udf/namei.c
index 9737cba1357d..fd6bfa153f49 100644
--- a/fs/udf/namei.c
+++ b/fs/udf/namei.c
@@ -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;
        }
diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c
index d7c6dbe4194b..ab94e6671849 100644
--- a/fs/udf/symlink.c
+++ b/fs/udf/symlink.c
@@ -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) {
@@ -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;
@@ -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)
@@ -100,8 +119,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);
diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h
index be7dabbbcb49..55d1d194d472 100644
--- a/fs/udf/udfdecl.h
+++ b/fs/udf/udfdecl.h
@@ -201,7 +201,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);
diff --git a/fs/udf/unicode.c b/fs/udf/unicode.c
index 44b815e57f94..d29c06fbf4ce 100644
--- a/fs/udf/unicode.c
+++ b/fs/udf/unicode.c
@@ -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)
 {
@@ -333,8 +334,8 @@ try_again:
        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;
@@ -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)) {
@@ -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);
@@ -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;
@@ -440,7 +444,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;
@@ -468,13 +472,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++] = hexChar[(valueCRC & 0xf000) >> 12];
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to