udf: limit the maximum number of indirect extents in a row
[pandora-kernel.git] / fs / udf / inode.c
index 2a706bb..5d67e20 100644 (file)
@@ -1271,6 +1271,7 @@ static void udf_fill_inode(struct inode *inode, struct buffer_head *bh)
        struct udf_sb_info *sbi = UDF_SB(inode->i_sb);
        struct udf_inode_info *iinfo = UDF_I(inode);
        unsigned int link_count;
+       int bs = inode->i_sb->s_blocksize;
 
        fe = (struct fileEntry *)bh->b_data;
        efe = (struct extendedFileEntry *)bh->b_data;
@@ -1291,41 +1292,38 @@ static void udf_fill_inode(struct inode *inode, struct buffer_head *bh)
        if (fe->descTag.tagIdent == cpu_to_le16(TAG_IDENT_EFE)) {
                iinfo->i_efe = 1;
                iinfo->i_use = 0;
-               if (udf_alloc_i_data(inode, inode->i_sb->s_blocksize -
+               if (udf_alloc_i_data(inode, bs -
                                        sizeof(struct extendedFileEntry))) {
                        make_bad_inode(inode);
                        return;
                }
                memcpy(iinfo->i_ext.i_data,
                       bh->b_data + sizeof(struct extendedFileEntry),
-                      inode->i_sb->s_blocksize -
-                                       sizeof(struct extendedFileEntry));
+                      bs - sizeof(struct extendedFileEntry));
        } else if (fe->descTag.tagIdent == cpu_to_le16(TAG_IDENT_FE)) {
                iinfo->i_efe = 0;
                iinfo->i_use = 0;
-               if (udf_alloc_i_data(inode, inode->i_sb->s_blocksize -
-                                               sizeof(struct fileEntry))) {
+               if (udf_alloc_i_data(inode, bs - sizeof(struct fileEntry))) {
                        make_bad_inode(inode);
                        return;
                }
                memcpy(iinfo->i_ext.i_data,
                       bh->b_data + sizeof(struct fileEntry),
-                      inode->i_sb->s_blocksize - sizeof(struct fileEntry));
+                      bs - sizeof(struct fileEntry));
        } else if (fe->descTag.tagIdent == cpu_to_le16(TAG_IDENT_USE)) {
                iinfo->i_efe = 0;
                iinfo->i_use = 1;
                iinfo->i_lenAlloc = le32_to_cpu(
                                ((struct unallocSpaceEntry *)bh->b_data)->
                                 lengthAllocDescs);
-               if (udf_alloc_i_data(inode, inode->i_sb->s_blocksize -
+               if (udf_alloc_i_data(inode, bs -
                                        sizeof(struct unallocSpaceEntry))) {
                        make_bad_inode(inode);
                        return;
                }
                memcpy(iinfo->i_ext.i_data,
                       bh->b_data + sizeof(struct unallocSpaceEntry),
-                      inode->i_sb->s_blocksize -
-                                       sizeof(struct unallocSpaceEntry));
+                      bs - sizeof(struct unallocSpaceEntry));
                return;
        }
 
@@ -1403,6 +1401,19 @@ static void udf_fill_inode(struct inode *inode, struct buffer_head *bh)
                                                        iinfo->i_lenEAttr;
        }
 
+       /*
+        * Sanity check length of allocation descriptors and extended attrs to
+        * avoid integer overflows
+        */
+       if (iinfo->i_lenEAttr > bs || iinfo->i_lenAlloc > bs) {
+               make_bad_inode(inode);
+               return;
+       }
+       /* Now do exact checks */
+       if (udf_file_entry_alloc_offset(inode) + iinfo->i_lenAlloc > bs) {
+               make_bad_inode(inode);
+               return;
+       }
        /* Sanity checks for files in ICB so that we don't get confused later */
        if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
                /*
@@ -1414,8 +1425,7 @@ static void udf_fill_inode(struct inode *inode, struct buffer_head *bh)
                        return;
                }
                /* File in ICB has to fit in there... */
-               if (inode->i_size > inode->i_sb->s_blocksize -
-                                       udf_file_entry_alloc_offset(inode)) {
+               if (inode->i_size > bs - udf_file_entry_alloc_offset(inode)) {
                        make_bad_inode(inode);
                        return;
                }
@@ -1965,14 +1975,29 @@ void udf_write_aext(struct inode *inode, struct extent_position *epos,
                epos->offset += adsize;
 }
 
+/*
+ * Only 1 indirect extent in a row really makes sense but allow upto 16 in case
+ * someone does some weird stuff.
+ */
+#define UDF_MAX_INDIR_EXTS 16
+
 int8_t udf_next_aext(struct inode *inode, struct extent_position *epos,
                     struct kernel_lb_addr *eloc, uint32_t *elen, int inc)
 {
        int8_t etype;
+       unsigned int indirections = 0;
 
        while ((etype = udf_current_aext(inode, epos, eloc, elen, inc)) ==
               (EXT_NEXT_EXTENT_ALLOCDECS >> 30)) {
                int block;
+
+               if (++indirections > UDF_MAX_INDIR_EXTS) {
+                       udf_err(inode->i_sb,
+                               "too many indirect extents in inode %lu\n",
+                               inode->i_ino);
+                       return -1;
+               }
+
                epos->block = *eloc;
                epos->offset = sizeof(struct allocExtDesc);
                brelse(epos->bh);