Improve extents verification logic
Add functionality for extents validation inside the filesystem extents block. The main logic is implemented under ext4_validate_extent_entries() function, which verifies extents or extents indexes depending of extent depth value. PR: 259112 Reported by: Robert Morris Reviewed by: pfg MFC after: 2 weeks Differential Revision: https://reviews.freebsd.org/D33375
This commit is contained in:
parent
ced2172822
commit
f1d5e2c862
@ -61,8 +61,10 @@ static MALLOC_DEFINE(M_EXT2EXTENTS, "ext2_extents", "EXT2 extents");
|
||||
#ifdef EXT2FS_PRINT_EXTENTS
|
||||
static const bool print_extents_walk = true;
|
||||
|
||||
static int ext4_ext_check_header(struct inode *, struct ext4_extent_header *);
|
||||
static int ext4_ext_walk_header(struct inode *, struct ext4_extent_header *);
|
||||
static int ext4_ext_check_header(struct inode *, struct ext4_extent_header *,
|
||||
int);
|
||||
static int ext4_ext_walk_header(struct inode *, struct ext4_extent_header *,
|
||||
int);
|
||||
static inline e4fs_daddr_t ext4_ext_index_pblock(struct ext4_extent_index *);
|
||||
static inline e4fs_daddr_t ext4_ext_extent_pblock(struct ext4_extent *);
|
||||
|
||||
@ -80,7 +82,8 @@ ext4_ext_blk_check(struct inode *ip, e4fs_daddr_t blk)
|
||||
}
|
||||
|
||||
static int
|
||||
ext4_ext_walk_index(struct inode *ip, struct ext4_extent_index *ex, bool do_walk)
|
||||
ext4_ext_walk_index(struct inode *ip, struct ext4_extent_index *ex, int depth,
|
||||
bool do_walk)
|
||||
{
|
||||
struct m_ext2fs *fs;
|
||||
struct buf *bp;
|
||||
@ -91,7 +94,8 @@ ext4_ext_walk_index(struct inode *ip, struct ext4_extent_index *ex, bool do_walk
|
||||
|
||||
if (print_extents_walk)
|
||||
printf(" index %p => (blk %u pblk %ju)\n", ex,
|
||||
le32toh(ex->ei_blk), (uint64_t)le16toh(ex->ei_leaf_hi) << 32 |
|
||||
le32toh(ex->ei_blk),
|
||||
(uint64_t)le16toh(ex->ei_leaf_hi) << 32 |
|
||||
le32toh(ex->ei_leaf_lo));
|
||||
|
||||
if(!do_walk)
|
||||
@ -108,7 +112,8 @@ ext4_ext_walk_index(struct inode *ip, struct ext4_extent_index *ex, bool do_walk
|
||||
return (error);
|
||||
}
|
||||
|
||||
error = ext4_ext_walk_header(ip, (struct ext4_extent_header *)bp->b_data);
|
||||
error = ext4_ext_walk_header(ip,
|
||||
(struct ext4_extent_header *)bp->b_data, depth);
|
||||
|
||||
brelse(bp);
|
||||
|
||||
@ -135,46 +140,28 @@ ext4_ext_walk_extent(struct inode *ip, struct ext4_extent *ep)
|
||||
}
|
||||
|
||||
static int
|
||||
ext4_ext_walk_header(struct inode *ip, struct ext4_extent_header *eh)
|
||||
ext4_ext_walk_header(struct inode *ip, struct ext4_extent_header *eh, int depth)
|
||||
{
|
||||
int i, error = 0;
|
||||
|
||||
error = ext4_ext_check_header(ip, eh);
|
||||
error = ext4_ext_check_header(ip, eh, depth);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
if (print_extents_walk)
|
||||
printf("header %p => (entries %d max %d depth %d gen %d)\n",
|
||||
eh, le16toh(eh->eh_ecount),
|
||||
le16toh(eh->eh_max), le16toh(eh->eh_depth), le32toh(eh->eh_gen));
|
||||
le16toh(eh->eh_max), le16toh(eh->eh_depth),
|
||||
le32toh(eh->eh_gen));
|
||||
|
||||
for (i = 0; i < le16toh(eh->eh_ecount) && error == 0; i++)
|
||||
if (eh->eh_depth != 0)
|
||||
error = ext4_ext_walk_index(ip,
|
||||
(struct ext4_extent_index *)(eh + 1 + i), true);
|
||||
(struct ext4_extent_index *)(eh + 1 + i), depth - 1,
|
||||
true);
|
||||
else
|
||||
error = ext4_ext_walk_extent(ip, (struct ext4_extent *)(eh + 1 + i));
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
ext4_ext_print_path(struct inode *ip, struct ext4_extent_path *path)
|
||||
{
|
||||
int k, l, error = 0;
|
||||
|
||||
l = path->ep_depth;
|
||||
|
||||
if (print_extents_walk)
|
||||
printf("ip=%ju, Path:\n", ip->i_number);
|
||||
|
||||
for (k = 0; k <= l && error == 0; k++, path++) {
|
||||
if (path->ep_index) {
|
||||
error = ext4_ext_walk_index(ip, path->ep_index, false);
|
||||
} else if (path->ep_ext) {
|
||||
error = ext4_ext_walk_extent(ip, path->ep_ext);
|
||||
}
|
||||
}
|
||||
error = ext4_ext_walk_extent(ip,
|
||||
(struct ext4_extent *)(eh + 1 + i));
|
||||
|
||||
return (error);
|
||||
}
|
||||
@ -192,7 +179,29 @@ ext4_ext_walk(struct inode *ip)
|
||||
if (!(ip->i_flag & IN_E4EXTENTS))
|
||||
return (0);
|
||||
|
||||
return (ext4_ext_walk_header(ip, ehp));
|
||||
return (ext4_ext_walk_header(ip, ehp, 0));
|
||||
}
|
||||
|
||||
static int
|
||||
ext4_ext_print_path(struct inode *ip, struct ext4_extent_path *path)
|
||||
{
|
||||
int k, depth, error = 0;
|
||||
|
||||
depth = path->ep_depth;
|
||||
|
||||
if (print_extents_walk)
|
||||
printf("ip=%ju, Path:\n", ip->i_number);
|
||||
|
||||
for (k = 0; k <= depth && error == 0; k++, path++) {
|
||||
if (path->ep_index) {
|
||||
error = ext4_ext_walk_index(ip, path->ep_index,
|
||||
depth - 1, false);
|
||||
} else if (path->ep_ext) {
|
||||
error = ext4_ext_walk_extent(ip, path->ep_ext);
|
||||
}
|
||||
}
|
||||
|
||||
return (error);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -277,8 +286,168 @@ ext4_ext_in_cache(struct inode *ip, daddr_t lbn, struct ext4_extent *ep)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static inline int
|
||||
ext4_ext_space_root(struct inode *ip)
|
||||
{
|
||||
int size;
|
||||
|
||||
size = sizeof(ip->i_data);
|
||||
size -= sizeof(struct ext4_extent_header);
|
||||
size /= sizeof(struct ext4_extent);
|
||||
|
||||
return (size);
|
||||
}
|
||||
|
||||
static inline int
|
||||
ext4_ext_space_block(struct inode *ip)
|
||||
{
|
||||
struct m_ext2fs *fs;
|
||||
int size;
|
||||
|
||||
fs = ip->i_e2fs;
|
||||
|
||||
size = (fs->e2fs_bsize - sizeof(struct ext4_extent_header)) /
|
||||
sizeof(struct ext4_extent);
|
||||
|
||||
return (size);
|
||||
}
|
||||
|
||||
static inline int
|
||||
ext4_ext_space_root_idx(struct inode *ip)
|
||||
{
|
||||
int size;
|
||||
|
||||
size = sizeof(ip->i_data);
|
||||
size -= sizeof(struct ext4_extent_header);
|
||||
size /= sizeof(struct ext4_extent_index);
|
||||
|
||||
return (size);
|
||||
}
|
||||
|
||||
static inline int
|
||||
ext4_ext_space_block_idx(struct inode *ip)
|
||||
{
|
||||
struct m_ext2fs *fs;
|
||||
int size;
|
||||
|
||||
fs = ip->i_e2fs;
|
||||
|
||||
size = (fs->e2fs_bsize - sizeof(struct ext4_extent_header)) /
|
||||
sizeof(struct ext4_extent_index);
|
||||
|
||||
return (size);
|
||||
}
|
||||
|
||||
static int
|
||||
ext4_ext_check_header(struct inode *ip, struct ext4_extent_header *eh)
|
||||
ext4_ext_max_entries(struct inode *ip, int depth)
|
||||
{
|
||||
|
||||
if (depth == ext4_ext_inode_depth(ip)) {
|
||||
if (depth == 0)
|
||||
return (ext4_ext_space_root(ip));
|
||||
else
|
||||
return (ext4_ext_space_root_idx(ip));
|
||||
} else {
|
||||
if (depth == 0)
|
||||
return (ext4_ext_space_block(ip));
|
||||
else
|
||||
return (ext4_ext_space_block_idx(ip));
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint16_t
|
||||
ext4_ext_get_actual_len(struct ext4_extent *ext)
|
||||
{
|
||||
|
||||
return (le16toh(ext->e_len) <= EXT_INIT_MAX_LEN ?
|
||||
le16toh(ext->e_len) : (le16toh(ext->e_len) - EXT_INIT_MAX_LEN));
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ext4_inode_block_validate(struct inode *ip, e4fs_daddr_t start_blk,
|
||||
unsigned int count)
|
||||
{
|
||||
struct m_ext2fs *fs;
|
||||
|
||||
fs = ip->i_e2fs;
|
||||
|
||||
if ((start_blk <= le32toh(fs->e2fs->e2fs_first_dblock)) ||
|
||||
(start_blk + count < start_blk) ||
|
||||
(start_blk + count > fs->e2fs_bcount))
|
||||
return (EIO);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
ext4_validate_extent(struct inode *ip, struct ext4_extent *ext)
|
||||
{
|
||||
e4fs_daddr_t blk = ext4_ext_extent_pblock(ext);
|
||||
uint32_t lblk = le32toh(ext->e_blk);
|
||||
int len = ext4_ext_get_actual_len(ext);
|
||||
|
||||
if (lblk + len <= lblk)
|
||||
return (EIO);
|
||||
|
||||
return (ext4_inode_block_validate(ip, blk, len));
|
||||
}
|
||||
|
||||
static int
|
||||
ext4_validate_extent_idx(struct inode *ip, struct ext4_extent_index *ext_idx)
|
||||
{
|
||||
e4fs_daddr_t blk = ext4_ext_index_pblock(ext_idx);
|
||||
|
||||
return (ext4_inode_block_validate(ip, blk, 1));
|
||||
}
|
||||
|
||||
static int
|
||||
ext4_validate_extent_entries(struct inode *ip, struct ext4_extent_header *eh,
|
||||
int depth)
|
||||
{
|
||||
unsigned int count;
|
||||
|
||||
count = le16toh(eh->eh_ecount);
|
||||
if (count == 0)
|
||||
return (0);
|
||||
|
||||
if (depth == 0) {
|
||||
struct ext4_extent *ext = EXT_FIRST_EXTENT(eh);
|
||||
uint32_t lblk = 0;
|
||||
uint32_t prev = 0;
|
||||
int len = 0;
|
||||
while (count) {
|
||||
/* leaf entries */
|
||||
if (ext4_validate_extent(ip, ext))
|
||||
return (EIO);
|
||||
|
||||
/* Check for overlapping extents */
|
||||
lblk = le32toh(ext->e_blk);
|
||||
len = ext4_ext_get_actual_len(ext);
|
||||
if ((lblk <= prev) && prev)
|
||||
return (EIO);
|
||||
|
||||
ext++;
|
||||
count--;
|
||||
prev = lblk + len - 1;
|
||||
}
|
||||
} else {
|
||||
struct ext4_extent_index *ext_idx = EXT_FIRST_INDEX(eh);
|
||||
while (count) {
|
||||
if (ext4_validate_extent_idx(ip, ext_idx))
|
||||
return (EIO);
|
||||
|
||||
ext_idx++;
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
ext4_ext_check_header(struct inode *ip, struct ext4_extent_header *eh,
|
||||
int depth)
|
||||
{
|
||||
char *error_msg;
|
||||
|
||||
@ -286,18 +455,32 @@ ext4_ext_check_header(struct inode *ip, struct ext4_extent_header *eh)
|
||||
error_msg = "header: invalid magic";
|
||||
goto corrupted;
|
||||
}
|
||||
if (le16toh(eh->eh_depth) != depth ||
|
||||
le16toh(eh->eh_depth) > EXT4_EXT_DEPTH_MAX)
|
||||
{
|
||||
error_msg = "header: invalid eh_depth";
|
||||
goto corrupted;
|
||||
}
|
||||
if (eh->eh_max == 0) {
|
||||
error_msg = "header: invalid eh_max";
|
||||
goto corrupted;
|
||||
}
|
||||
if (le16toh(eh->eh_max) > ext4_ext_max_entries(ip, depth)) {
|
||||
error_msg = "header: too large eh_max";
|
||||
goto corrupted;
|
||||
}
|
||||
if (le16toh(eh->eh_ecount) > le16toh(eh->eh_max)) {
|
||||
error_msg = "header: invalid eh_entries";
|
||||
goto corrupted;
|
||||
}
|
||||
if (eh->eh_depth > 5) {
|
||||
if (le16toh(eh->eh_depth) > EXT4_EXT_DEPTH_MAX) {
|
||||
error_msg = "header: invalid eh_depth";
|
||||
goto corrupted;
|
||||
}
|
||||
if (ext4_validate_extent_entries(ip, eh, depth)) {
|
||||
error_msg = "header: invalid extent entries";
|
||||
goto corrupted;
|
||||
}
|
||||
|
||||
return (0);
|
||||
|
||||
@ -426,7 +609,7 @@ ext4_ext_find_extent(struct inode *ip, daddr_t block,
|
||||
ppos = 0;
|
||||
alloc = 0;
|
||||
|
||||
error = ext4_ext_check_header(ip, eh);
|
||||
error = ext4_ext_check_header(ip, eh, depth);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
@ -472,7 +655,7 @@ ext4_ext_find_extent(struct inode *ip, daddr_t block,
|
||||
bqrelse(bp);
|
||||
|
||||
eh = ext4_ext_block_header(path[ppos].ep_data);
|
||||
if (ext4_ext_check_header(ip, eh) ||
|
||||
if (ext4_ext_check_header(ip, eh, i - 1) ||
|
||||
ext2_extent_blk_csum_verify(ip, path[ppos].ep_data)) {
|
||||
error = EIO;
|
||||
goto error;
|
||||
@ -483,7 +666,7 @@ ext4_ext_find_extent(struct inode *ip, daddr_t block,
|
||||
i--;
|
||||
}
|
||||
|
||||
error = ext4_ext_check_header(ip, eh);
|
||||
error = ext4_ext_check_header(ip, eh, 0);
|
||||
if (error)
|
||||
goto error;
|
||||
|
||||
@ -505,32 +688,6 @@ error:
|
||||
return (error);
|
||||
}
|
||||
|
||||
static inline int
|
||||
ext4_ext_space_root(struct inode *ip)
|
||||
{
|
||||
int size;
|
||||
|
||||
size = sizeof(ip->i_data);
|
||||
size -= sizeof(struct ext4_extent_header);
|
||||
size /= sizeof(struct ext4_extent);
|
||||
|
||||
return (size);
|
||||
}
|
||||
|
||||
static inline int
|
||||
ext4_ext_space_block(struct inode *ip)
|
||||
{
|
||||
struct m_ext2fs *fs;
|
||||
int size;
|
||||
|
||||
fs = ip->i_e2fs;
|
||||
|
||||
size = (fs->e2fs_bsize - sizeof(struct ext4_extent_header)) /
|
||||
sizeof(struct ext4_extent);
|
||||
|
||||
return (size);
|
||||
}
|
||||
|
||||
static inline int
|
||||
ext4_ext_space_block_index(struct inode *ip)
|
||||
{
|
||||
@ -1339,14 +1496,6 @@ out2:
|
||||
return (error);
|
||||
}
|
||||
|
||||
static inline uint16_t
|
||||
ext4_ext_get_actual_len(struct ext4_extent *ext)
|
||||
{
|
||||
|
||||
return (le16toh(ext->e_len) <= EXT_INIT_MAX_LEN ?
|
||||
le16toh(ext->e_len) : (le16toh(ext->e_len) - EXT_INIT_MAX_LEN));
|
||||
}
|
||||
|
||||
static inline struct ext4_extent_header *
|
||||
ext4_ext_header(struct inode *ip)
|
||||
{
|
||||
@ -1503,7 +1652,7 @@ ext4_read_extent_tree_block(struct inode *ip, e4fs_daddr_t pblk,
|
||||
goto err;
|
||||
}
|
||||
|
||||
error = ext4_ext_check_header(ip, eh);
|
||||
error = ext4_ext_check_header(ip, eh, depth);
|
||||
if (error)
|
||||
goto err;
|
||||
|
||||
@ -1544,7 +1693,7 @@ ext4_ext_remove_space(struct inode *ip, off_t length, int flags,
|
||||
ehp = (struct ext4_extent_header *)ip->i_db;
|
||||
depth = ext4_ext_inode_depth(ip);
|
||||
|
||||
error = ext4_ext_check_header(ip, ehp);
|
||||
error = ext4_ext_check_header(ip, ehp, depth);
|
||||
if(error)
|
||||
return (error);
|
||||
|
||||
|
@ -65,11 +65,11 @@ ext2_print_inode(struct inode *in)
|
||||
in->i_uid, in->i_gid, (uintmax_t)in->i_size);
|
||||
printf("Links: %3d Blockcount: %ju\n",
|
||||
in->i_nlink, (uintmax_t)in->i_blocks);
|
||||
printf("ctime: 0x%x ", in->i_ctime);
|
||||
printf("atime: 0x%x ", in->i_atime);
|
||||
printf("mtime: 0x%x ", in->i_mtime);
|
||||
printf("ctime: 0x%llx ", (unsigned long long)in->i_ctime);
|
||||
printf("atime: 0x%llx ", (unsigned long long)in->i_atime);
|
||||
printf("mtime: 0x%llx ", (unsigned long long)in->i_mtime);
|
||||
if (E2DI_HAS_XTIME(in))
|
||||
printf("crtime %#x\n", in->i_birthtime);
|
||||
printf("crtime %llx\n", (unsigned long long)in->i_birthtime);
|
||||
else
|
||||
printf("\n");
|
||||
if (in->i_flag & IN_E4EXTENTS) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user