nilfs2: verify metadata sizes read from disk
authorRyusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
Thu, 3 Apr 2014 21:50:31 +0000 (14:50 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 3 Apr 2014 23:21:26 +0000 (16:21 -0700)
Add code to check sizes of on-disk data of metadata files such as inode
size, segment usage size, DAT entry size, and checkpoint size.  Although
these sizes are read from disk, the current implementation doesn't check
them.

If these sizes are not sane on disk, it can cause out-of-range access to
metadata or memory access overrun on metadata block buffers due to
overflow in sundry calculations.

Both lower limit and upper limit of metadata sizes are verified to
prevent these issues.

Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
Cc: Andreas Rohner <andreas.rohner@gmx.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
fs/nilfs2/cpfile.c
fs/nilfs2/dat.c
fs/nilfs2/sufile.c
fs/nilfs2/the_nilfs.c
include/linux/nilfs2_fs.h

index deaa3d33a0aafa7a6cc303a263288784d03e002f..0d58075f34e29da6f666b008afdcaa74596b0255 100644 (file)
@@ -942,6 +942,18 @@ int nilfs_cpfile_read(struct super_block *sb, size_t cpsize,
        struct inode *cpfile;
        int err;
 
+       if (cpsize > sb->s_blocksize) {
+               printk(KERN_ERR
+                      "NILFS: too large checkpoint size: %zu bytes.\n",
+                      cpsize);
+               return -EINVAL;
+       } else if (cpsize < NILFS_MIN_CHECKPOINT_SIZE) {
+               printk(KERN_ERR
+                      "NILFS: too small checkpoint size: %zu bytes.\n",
+                      cpsize);
+               return -EINVAL;
+       }
+
        cpfile = nilfs_iget_locked(sb, NULL, NILFS_CPFILE_INO);
        if (unlikely(!cpfile))
                return -ENOMEM;
index fa0f80308c2dce70d89bf6e459119c9b18543d27..0d5fada9119136e793b73cf9d008bae29e43fcf8 100644 (file)
@@ -484,6 +484,18 @@ int nilfs_dat_read(struct super_block *sb, size_t entry_size,
        struct nilfs_dat_info *di;
        int err;
 
+       if (entry_size > sb->s_blocksize) {
+               printk(KERN_ERR
+                      "NILFS: too large DAT entry size: %zu bytes.\n",
+                      entry_size);
+               return -EINVAL;
+       } else if (entry_size < NILFS_MIN_DAT_ENTRY_SIZE) {
+               printk(KERN_ERR
+                      "NILFS: too small DAT entry size: %zu bytes.\n",
+                      entry_size);
+               return -EINVAL;
+       }
+
        dat = nilfs_iget_locked(sb, NULL, NILFS_DAT_INO);
        if (unlikely(!dat))
                return -ENOMEM;
index 84e384dae663e7826577530a403f90c3ee5ff70f..2a869c35c3622386ac0fdc774681df65c59f7093 100644 (file)
@@ -1169,6 +1169,18 @@ int nilfs_sufile_read(struct super_block *sb, size_t susize,
        void *kaddr;
        int err;
 
+       if (susize > sb->s_blocksize) {
+               printk(KERN_ERR
+                      "NILFS: too large segment usage size: %zu bytes.\n",
+                      susize);
+               return -EINVAL;
+       } else if (susize < NILFS_MIN_SEGMENT_USAGE_SIZE) {
+               printk(KERN_ERR
+                      "NILFS: too small segment usage size: %zu bytes.\n",
+                      susize);
+               return -EINVAL;
+       }
+
        sufile = nilfs_iget_locked(sb, NULL, NILFS_SUFILE_INO);
        if (unlikely(!sufile))
                return -ENOMEM;
index 94c451ce6d247b5ca0e284a8667308d6509d0741..8ba8229ba076a0de225b1d95bd26d2c51ee656c3 100644 (file)
@@ -399,6 +399,16 @@ static int nilfs_store_disk_layout(struct the_nilfs *nilfs,
                return -EINVAL;
 
        nilfs->ns_inode_size = le16_to_cpu(sbp->s_inode_size);
+       if (nilfs->ns_inode_size > nilfs->ns_blocksize) {
+               printk(KERN_ERR "NILFS: too large inode size: %d bytes.\n",
+                      nilfs->ns_inode_size);
+               return -EINVAL;
+       } else if (nilfs->ns_inode_size < NILFS_MIN_INODE_SIZE) {
+               printk(KERN_ERR "NILFS: too small inode size: %d bytes.\n",
+                      nilfs->ns_inode_size);
+               return -EINVAL;
+       }
+
        nilfs->ns_first_ino = le32_to_cpu(sbp->s_first_ino);
 
        nilfs->ns_blocks_per_segment = le32_to_cpu(sbp->s_blocks_per_segment);
index 1fb465f9baf275c50d84c4dee7d8dac5b943f6ca..ff3fea3194c6a05e291de61df2cae02cec8d5348 100644 (file)
@@ -82,6 +82,8 @@ struct nilfs_inode {
        __le32  i_pad;
 };
 
+#define NILFS_MIN_INODE_SIZE           128
+
 /**
  * struct nilfs_super_root - structure of super root
  * @sr_sum: check sum
@@ -482,6 +484,8 @@ struct nilfs_dat_entry {
        __le64 de_rsv;
 };
 
+#define NILFS_MIN_DAT_ENTRY_SIZE       32
+
 /**
  * struct nilfs_snapshot_list - snapshot list
  * @ssl_next: next checkpoint number on snapshot list
@@ -520,6 +524,8 @@ struct nilfs_checkpoint {
        struct nilfs_inode cp_ifile_inode;
 };
 
+#define NILFS_MIN_CHECKPOINT_SIZE      (64 + NILFS_MIN_INODE_SIZE)
+
 /* checkpoint flags */
 enum {
        NILFS_CHECKPOINT_SNAPSHOT,
@@ -615,6 +621,8 @@ struct nilfs_segment_usage {
        __le32 su_flags;
 };
 
+#define NILFS_MIN_SEGMENT_USAGE_SIZE   16
+
 /* segment usage flag */
 enum {
        NILFS_SEGMENT_USAGE_ACTIVE,