Skip to content

Commit

Permalink
nilfs2: verify metadata sizes read from disk
Browse files Browse the repository at this point in the history
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 <[email protected]>
Cc: Andreas Rohner <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
konis authored and torvalds committed Apr 3, 2014
1 parent f9f32c4 commit 0ec060d
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 0 deletions.
12 changes: 12 additions & 0 deletions fs/nilfs2/cpfile.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
12 changes: 12 additions & 0 deletions fs/nilfs2/dat.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
12 changes: 12 additions & 0 deletions fs/nilfs2/sufile.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 10 additions & 0 deletions fs/nilfs2/the_nilfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 8 additions & 0 deletions include/linux/nilfs2_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 0ec060d

Please sign in to comment.