Skip to content

Commit 642335f

Browse files
thejhtytso
authored andcommitted
ext4: don't treat fhandle lookup of ea_inode as FS corruption
A file handle that userspace provides to open_by_handle_at() can legitimately contain an outdated inode number that has since been reused for another purpose - that's why the file handle also contains a generation number. But if the inode number has been reused for an ea_inode, check_igot_inode() will notice, __ext4_iget() will go through ext4_error_inode(), and if the inode was newly created, it will also be marked as bad by iget_failed(). This all happens before the point where the inode generation is checked. ext4_error_inode() is supposed to only be used on filesystem corruption; it should not be used when userspace just got unlucky with a stale file handle. So when this happens, let __ext4_iget() just return an error. Fixes: b3e6bcb ("ext4: add EA_INODE checking to ext4_iget()") Signed-off-by: Jann Horn <jannh@google.com> Reviewed-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20241129-ext4-ignore-ea-fhandle-v1-1-e532c0d1cee0@google.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
1 parent d5e2067 commit 642335f

File tree

1 file changed

+48
-20
lines changed

1 file changed

+48
-20
lines changed

fs/ext4/inode.c

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4732,22 +4732,43 @@ static inline void ext4_inode_set_iversion_queried(struct inode *inode, u64 val)
47324732
inode_set_iversion_queried(inode, val);
47334733
}
47344734

4735-
static const char *check_igot_inode(struct inode *inode, ext4_iget_flags flags)
4736-
4735+
static int check_igot_inode(struct inode *inode, ext4_iget_flags flags,
4736+
const char *function, unsigned int line)
47374737
{
4738+
const char *err_str;
4739+
47384740
if (flags & EXT4_IGET_EA_INODE) {
4739-
if (!(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL))
4740-
return "missing EA_INODE flag";
4741+
if (!(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) {
4742+
err_str = "missing EA_INODE flag";
4743+
goto error;
4744+
}
47414745
if (ext4_test_inode_state(inode, EXT4_STATE_XATTR) ||
4742-
EXT4_I(inode)->i_file_acl)
4743-
return "ea_inode with extended attributes";
4746+
EXT4_I(inode)->i_file_acl) {
4747+
err_str = "ea_inode with extended attributes";
4748+
goto error;
4749+
}
47444750
} else {
4745-
if ((EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL))
4746-
return "unexpected EA_INODE flag";
4751+
if ((EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) {
4752+
/*
4753+
* open_by_handle_at() could provide an old inode number
4754+
* that has since been reused for an ea_inode; this does
4755+
* not indicate filesystem corruption
4756+
*/
4757+
if (flags & EXT4_IGET_HANDLE)
4758+
return -ESTALE;
4759+
err_str = "unexpected EA_INODE flag";
4760+
goto error;
4761+
}
4762+
}
4763+
if (is_bad_inode(inode) && !(flags & EXT4_IGET_BAD)) {
4764+
err_str = "unexpected bad inode w/o EXT4_IGET_BAD";
4765+
goto error;
47474766
}
4748-
if (is_bad_inode(inode) && !(flags & EXT4_IGET_BAD))
4749-
return "unexpected bad inode w/o EXT4_IGET_BAD";
4750-
return NULL;
4767+
return 0;
4768+
4769+
error:
4770+
ext4_error_inode(inode, function, line, 0, err_str);
4771+
return -EFSCORRUPTED;
47514772
}
47524773

47534774
struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
@@ -4759,7 +4780,6 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
47594780
struct ext4_inode_info *ei;
47604781
struct ext4_super_block *es = EXT4_SB(sb)->s_es;
47614782
struct inode *inode;
4762-
const char *err_str;
47634783
journal_t *journal = EXT4_SB(sb)->s_journal;
47644784
long ret;
47654785
loff_t size;
@@ -4788,10 +4808,10 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
47884808
if (!inode)
47894809
return ERR_PTR(-ENOMEM);
47904810
if (!(inode->i_state & I_NEW)) {
4791-
if ((err_str = check_igot_inode(inode, flags)) != NULL) {
4792-
ext4_error_inode(inode, function, line, 0, err_str);
4811+
ret = check_igot_inode(inode, flags, function, line);
4812+
if (ret) {
47934813
iput(inode);
4794-
return ERR_PTR(-EFSCORRUPTED);
4814+
return ERR_PTR(ret);
47954815
}
47964816
return inode;
47974817
}
@@ -5073,13 +5093,21 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
50735093
ret = -EFSCORRUPTED;
50745094
goto bad_inode;
50755095
}
5076-
if ((err_str = check_igot_inode(inode, flags)) != NULL) {
5077-
ext4_error_inode(inode, function, line, 0, err_str);
5078-
ret = -EFSCORRUPTED;
5079-
goto bad_inode;
5096+
ret = check_igot_inode(inode, flags, function, line);
5097+
/*
5098+
* -ESTALE here means there is nothing inherently wrong with the inode,
5099+
* it's just not an inode we can return for an fhandle lookup.
5100+
*/
5101+
if (ret == -ESTALE) {
5102+
brelse(iloc.bh);
5103+
unlock_new_inode(inode);
5104+
iput(inode);
5105+
return ERR_PTR(-ESTALE);
50805106
}
5081-
5107+
if (ret)
5108+
goto bad_inode;
50825109
brelse(iloc.bh);
5110+
50835111
unlock_new_inode(inode);
50845112
return inode;
50855113

0 commit comments

Comments
 (0)