Skip to content

Commit 51416ce

Browse files
committed
ext4: do not create EA inode under buffer lock
JIRA: https://issues.redhat.com/browse/RHEL-48282 Tested: with xfstests CVE: CVE-2024-40972 ext4_xattr_set_entry() creates new EA inodes while holding buffer lock on the external xattr block. This is problematic as it nests all the allocation locking (which acquires locks on other buffers) under the buffer lock. This can even deadlock when the filesystem is corrupted and e.g. quota file is setup to contain xattr block as data block. Move the allocation of EA inode out of ext4_xattr_set_entry() into the callers. Reported-by: syzbot+a43d4f48b8397d0e41a9@syzkaller.appspotmail.com Signed-off-by: Jan Kara <jack@suse.cz> Link: https://lore.kernel.org/r/20240321162657.27420-2-jack@suse.cz Signed-off-by: Theodore Ts'o <tytso@mit.edu> (cherry picked from commit 0a46ef2) Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com>
1 parent f41753f commit 51416ce

File tree

1 file changed

+53
-60
lines changed

1 file changed

+53
-60
lines changed

fs/ext4/xattr.c

Lines changed: 53 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1620,14 +1620,14 @@ static struct inode *ext4_xattr_inode_lookup_create(handle_t *handle,
16201620
static int ext4_xattr_set_entry(struct ext4_xattr_info *i,
16211621
struct ext4_xattr_search *s,
16221622
handle_t *handle, struct inode *inode,
1623+
struct inode *new_ea_inode,
16231624
bool is_block)
16241625
{
16251626
struct ext4_xattr_entry *last, *next;
16261627
struct ext4_xattr_entry *here = s->here;
16271628
size_t min_offs = s->end - s->base, name_len = strlen(i->name);
16281629
int in_inode = i->in_inode;
16291630
struct inode *old_ea_inode = NULL;
1630-
struct inode *new_ea_inode = NULL;
16311631
size_t old_size, new_size;
16321632
int ret;
16331633

@@ -1712,38 +1712,11 @@ static int ext4_xattr_set_entry(struct ext4_xattr_info *i,
17121712
old_ea_inode = NULL;
17131713
goto out;
17141714
}
1715-
}
1716-
if (i->value && in_inode) {
1717-
WARN_ON_ONCE(!i->value_len);
1718-
1719-
new_ea_inode = ext4_xattr_inode_lookup_create(handle, inode,
1720-
i->value, i->value_len);
1721-
if (IS_ERR(new_ea_inode)) {
1722-
ret = PTR_ERR(new_ea_inode);
1723-
new_ea_inode = NULL;
1724-
goto out;
1725-
}
1726-
}
17271715

1728-
if (old_ea_inode) {
17291716
/* We are ready to release ref count on the old_ea_inode. */
17301717
ret = ext4_xattr_inode_dec_ref(handle, old_ea_inode);
1731-
if (ret) {
1732-
/* Release newly required ref count on new_ea_inode. */
1733-
if (new_ea_inode) {
1734-
int err;
1735-
1736-
err = ext4_xattr_inode_dec_ref(handle,
1737-
new_ea_inode);
1738-
if (err)
1739-
ext4_warning_inode(new_ea_inode,
1740-
"dec ref new_ea_inode err=%d",
1741-
err);
1742-
ext4_xattr_inode_free_quota(inode, new_ea_inode,
1743-
i->value_len);
1744-
}
1718+
if (ret)
17451719
goto out;
1746-
}
17471720

17481721
ext4_xattr_inode_free_quota(inode, old_ea_inode,
17491722
le32_to_cpu(here->e_value_size));
@@ -1853,7 +1826,6 @@ static int ext4_xattr_set_entry(struct ext4_xattr_info *i,
18531826
ret = 0;
18541827
out:
18551828
iput(old_ea_inode);
1856-
iput(new_ea_inode);
18571829
return ret;
18581830
}
18591831

@@ -1916,9 +1888,21 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
19161888
size_t old_ea_inode_quota = 0;
19171889
unsigned int ea_ino;
19181890

1919-
19201891
#define header(x) ((struct ext4_xattr_header *)(x))
19211892

1893+
/* If we need EA inode, prepare it before locking the buffer */
1894+
if (i->value && i->in_inode) {
1895+
WARN_ON_ONCE(!i->value_len);
1896+
1897+
ea_inode = ext4_xattr_inode_lookup_create(handle, inode,
1898+
i->value, i->value_len);
1899+
if (IS_ERR(ea_inode)) {
1900+
error = PTR_ERR(ea_inode);
1901+
ea_inode = NULL;
1902+
goto cleanup;
1903+
}
1904+
}
1905+
19221906
if (s->base) {
19231907
int offset = (char *)s->here - bs->bh->b_data;
19241908

@@ -1927,6 +1911,7 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
19271911
EXT4_JTR_NONE);
19281912
if (error)
19291913
goto cleanup;
1914+
19301915
lock_buffer(bs->bh);
19311916

19321917
if (header(s->base)->h_refcount == cpu_to_le32(1)) {
@@ -1953,7 +1938,7 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
19531938
}
19541939
ea_bdebug(bs->bh, "modifying in-place");
19551940
error = ext4_xattr_set_entry(i, s, handle, inode,
1956-
true /* is_block */);
1941+
ea_inode, true /* is_block */);
19571942
ext4_xattr_block_csum_set(inode, bs->bh);
19581943
unlock_buffer(bs->bh);
19591944
if (error == -EFSCORRUPTED)
@@ -2021,29 +2006,13 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
20212006
s->end = s->base + sb->s_blocksize;
20222007
}
20232008

2024-
error = ext4_xattr_set_entry(i, s, handle, inode, true /* is_block */);
2009+
error = ext4_xattr_set_entry(i, s, handle, inode, ea_inode,
2010+
true /* is_block */);
20252011
if (error == -EFSCORRUPTED)
20262012
goto bad_block;
20272013
if (error)
20282014
goto cleanup;
20292015

2030-
if (i->value && s->here->e_value_inum) {
2031-
/*
2032-
* A ref count on ea_inode has been taken as part of the call to
2033-
* ext4_xattr_set_entry() above. We would like to drop this
2034-
* extra ref but we have to wait until the xattr block is
2035-
* initialized and has its own ref count on the ea_inode.
2036-
*/
2037-
ea_ino = le32_to_cpu(s->here->e_value_inum);
2038-
error = ext4_xattr_inode_iget(inode, ea_ino,
2039-
le32_to_cpu(s->here->e_hash),
2040-
&ea_inode);
2041-
if (error) {
2042-
ea_inode = NULL;
2043-
goto cleanup;
2044-
}
2045-
}
2046-
20472016
inserted:
20482017
if (!IS_LAST_ENTRY(s->first)) {
20492018
new_bh = ext4_xattr_block_cache_find(inode, header(s->base),
@@ -2196,17 +2165,16 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
21962165

21972166
cleanup:
21982167
if (ea_inode) {
2199-
int error2;
2200-
2201-
error2 = ext4_xattr_inode_dec_ref(handle, ea_inode);
2202-
if (error2)
2203-
ext4_warning_inode(ea_inode, "dec ref error=%d",
2204-
error2);
2168+
if (error) {
2169+
int error2;
22052170

2206-
/* If there was an error, revert the quota charge. */
2207-
if (error)
2171+
error2 = ext4_xattr_inode_dec_ref(handle, ea_inode);
2172+
if (error2)
2173+
ext4_warning_inode(ea_inode, "dec ref error=%d",
2174+
error2);
22082175
ext4_xattr_inode_free_quota(inode, ea_inode,
22092176
i_size_read(ea_inode));
2177+
}
22102178
iput(ea_inode);
22112179
}
22122180
if (ce)
@@ -2264,14 +2232,38 @@ int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
22642232
{
22652233
struct ext4_xattr_ibody_header *header;
22662234
struct ext4_xattr_search *s = &is->s;
2235+
struct inode *ea_inode = NULL;
22672236
int error;
22682237

22692238
if (!EXT4_INODE_HAS_XATTR_SPACE(inode))
22702239
return -ENOSPC;
22712240

2272-
error = ext4_xattr_set_entry(i, s, handle, inode, false /* is_block */);
2273-
if (error)
2241+
/* If we need EA inode, prepare it before locking the buffer */
2242+
if (i->value && i->in_inode) {
2243+
WARN_ON_ONCE(!i->value_len);
2244+
2245+
ea_inode = ext4_xattr_inode_lookup_create(handle, inode,
2246+
i->value, i->value_len);
2247+
if (IS_ERR(ea_inode))
2248+
return PTR_ERR(ea_inode);
2249+
}
2250+
error = ext4_xattr_set_entry(i, s, handle, inode, ea_inode,
2251+
false /* is_block */);
2252+
if (error) {
2253+
if (ea_inode) {
2254+
int error2;
2255+
2256+
error2 = ext4_xattr_inode_dec_ref(handle, ea_inode);
2257+
if (error2)
2258+
ext4_warning_inode(ea_inode, "dec ref error=%d",
2259+
error2);
2260+
2261+
ext4_xattr_inode_free_quota(inode, ea_inode,
2262+
i_size_read(ea_inode));
2263+
iput(ea_inode);
2264+
}
22742265
return error;
2266+
}
22752267
header = IHDR(inode, ext4_raw_inode(&is->iloc));
22762268
if (!IS_LAST_ENTRY(s->first)) {
22772269
header->h_magic = cpu_to_le32(EXT4_XATTR_MAGIC);
@@ -2280,6 +2272,7 @@ int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
22802272
header->h_magic = cpu_to_le32(0);
22812273
ext4_clear_inode_state(inode, EXT4_STATE_XATTR);
22822274
}
2275+
iput(ea_inode);
22832276
return 0;
22842277
}
22852278

0 commit comments

Comments
 (0)