Dear Ted,
this follows your suggestion from 2009-02-05. The patch is done
analogous to Aneesh Kumar's patches for extent validation. It is based
on 2.6.29-rc7 with Aneesh's patches:
ext4: Add checks to validate extent entries
ext4: Validate extent details only when read from the disk
Kind regards,
Signed-off-by: Thiemo Nagel <thiemo.nagel@xxxxxxxxx>
--- download/linux-2.6.29-rc7-vanilla-extcheck/fs/ext4/inode.c 2009-03-12 16:10:31.000000000 +0100
+++ linux-2.6.29-rc7/fs/ext4/inode.c 2009-03-12 16:09:27.000000000 +0100
@@ -371,6 +371,34 @@
return n;
}
+static int __ext4_check_blockref(const char *function, struct inode *inode,
+ unsigned int *p, unsigned int max) {
+
+ struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es;
+ unsigned int offset = 0;
+ while (offset < max && p[offset]) {
+ if (unlikely(p[offset] < le32_to_cpu(es->s_first_data_block) ||
+ (p[offset] > ext4_blocks_count(es)))) {
+ ext4_error(inode->i_sb, function,
+ "bad block reference in inode #%lu, "
+ "offset=%u, blockref=%u",
+ inode->i_ino, offset, p[offset]);
+ return -EIO;
+ }
+ offset++;
+ }
+ return 0;
+}
+
+
+#define ext4_check_indirect_blockref(inode, bh) \
+ __ext4_check_blockref(__func__, inode, (__le32 *)(bh)->b_data, \
+ EXT4_ADDR_PER_BLOCK((inode)->i_sb))
+
+#define ext4_check_inode_blockref(inode) \
+ __ext4_check_blockref(__func__, inode, EXT4_I(inode)->i_data, \
+ EXT4_NDIR_BLOCKS)
+
/**
* ext4_get_branch - read the chain of indirect blocks leading to data
* @inode: inode in question
@@ -418,6 +446,9 @@
bh = sb_bread(sb, le32_to_cpu(p->key));
if (!bh)
goto failure;
+ if (ext4_check_indirect_blockref(inode, bh))
+ goto failure;
+
add_chain(++p, bh, (__le32 *)bh->b_data + *++offsets);
/* Reader: end */
if (!p->key)
@@ -4302,11 +4333,13 @@
if (ei->i_flags & EXT4_EXTENTS_FL) {
/* Validate extent which is part of inode */
ret = ext4_ext_check_inode(inode);
- if (ret) {
- brelse(bh);
- goto bad_inode;
- }
-
+ } else {
+ /* Validate block references which are part of inode */
+ ret = ext4_check_inode_blockref(inode);
+ }
+ if (ret) {
+ brelse(bh);
+ goto bad_inode;
}
if (S_ISREG(inode->i_mode)) {