ext4: do not use extent after put_bh
ext4_ext_search_right() will read more extent blocks and call put_bh after we get the information we need. However, ret_ex will break this and may cause use-after-free once pagecache has been freed. Fix it by copying the extent structure if needed. Signed-off-by: yangerkun <yangerkun@huawei.com> Link: https://lore.kernel.org/r/20201028055617.2569255-1-yangerkun@huawei.com Signed-off-by: Theodore Ts'o <tytso@mit.edu> Cc: stable@kernel.org
This commit is contained in:
parent
8c9be1e58a
commit
d7dce9e085
@ -1471,16 +1471,16 @@ static int ext4_ext_search_left(struct inode *inode,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* search the closest allocated block to the right for *logical
|
* Search the closest allocated block to the right for *logical
|
||||||
* and returns it at @logical + it's physical address at @phys
|
* and returns it at @logical + it's physical address at @phys.
|
||||||
* if *logical is the largest allocated block, the function
|
* If not exists, return 0 and @phys is set to 0. We will return
|
||||||
* returns 0 at @phys
|
* 1 which means we found an allocated block and ret_ex is valid.
|
||||||
* return value contains 0 (success) or error code
|
* Or return a (< 0) error code.
|
||||||
*/
|
*/
|
||||||
static int ext4_ext_search_right(struct inode *inode,
|
static int ext4_ext_search_right(struct inode *inode,
|
||||||
struct ext4_ext_path *path,
|
struct ext4_ext_path *path,
|
||||||
ext4_lblk_t *logical, ext4_fsblk_t *phys,
|
ext4_lblk_t *logical, ext4_fsblk_t *phys,
|
||||||
struct ext4_extent **ret_ex)
|
struct ext4_extent *ret_ex)
|
||||||
{
|
{
|
||||||
struct buffer_head *bh = NULL;
|
struct buffer_head *bh = NULL;
|
||||||
struct ext4_extent_header *eh;
|
struct ext4_extent_header *eh;
|
||||||
@ -1574,10 +1574,11 @@ got_index:
|
|||||||
found_extent:
|
found_extent:
|
||||||
*logical = le32_to_cpu(ex->ee_block);
|
*logical = le32_to_cpu(ex->ee_block);
|
||||||
*phys = ext4_ext_pblock(ex);
|
*phys = ext4_ext_pblock(ex);
|
||||||
*ret_ex = ex;
|
if (ret_ex)
|
||||||
|
*ret_ex = *ex;
|
||||||
if (bh)
|
if (bh)
|
||||||
put_bh(bh);
|
put_bh(bh);
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2868,8 +2869,8 @@ again:
|
|||||||
*/
|
*/
|
||||||
lblk = ex_end + 1;
|
lblk = ex_end + 1;
|
||||||
err = ext4_ext_search_right(inode, path, &lblk, &pblk,
|
err = ext4_ext_search_right(inode, path, &lblk, &pblk,
|
||||||
&ex);
|
NULL);
|
||||||
if (err)
|
if (err < 0)
|
||||||
goto out;
|
goto out;
|
||||||
if (pblk) {
|
if (pblk) {
|
||||||
partial.pclu = EXT4_B2C(sbi, pblk);
|
partial.pclu = EXT4_B2C(sbi, pblk);
|
||||||
@ -4039,7 +4040,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
|
|||||||
struct ext4_map_blocks *map, int flags)
|
struct ext4_map_blocks *map, int flags)
|
||||||
{
|
{
|
||||||
struct ext4_ext_path *path = NULL;
|
struct ext4_ext_path *path = NULL;
|
||||||
struct ext4_extent newex, *ex, *ex2;
|
struct ext4_extent newex, *ex, ex2;
|
||||||
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
||||||
ext4_fsblk_t newblock = 0, pblk;
|
ext4_fsblk_t newblock = 0, pblk;
|
||||||
int err = 0, depth, ret;
|
int err = 0, depth, ret;
|
||||||
@ -4175,15 +4176,14 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
|
|||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
ar.lright = map->m_lblk;
|
ar.lright = map->m_lblk;
|
||||||
ex2 = NULL;
|
|
||||||
err = ext4_ext_search_right(inode, path, &ar.lright, &ar.pright, &ex2);
|
err = ext4_ext_search_right(inode, path, &ar.lright, &ar.pright, &ex2);
|
||||||
if (err)
|
if (err < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/* Check if the extent after searching to the right implies a
|
/* Check if the extent after searching to the right implies a
|
||||||
* cluster we can use. */
|
* cluster we can use. */
|
||||||
if ((sbi->s_cluster_ratio > 1) && ex2 &&
|
if ((sbi->s_cluster_ratio > 1) && err &&
|
||||||
get_implied_cluster_alloc(inode->i_sb, map, ex2, path)) {
|
get_implied_cluster_alloc(inode->i_sb, map, &ex2, path)) {
|
||||||
ar.len = allocated = map->m_len;
|
ar.len = allocated = map->m_len;
|
||||||
newblock = map->m_pblk;
|
newblock = map->m_pblk;
|
||||||
goto got_allocated_blocks;
|
goto got_allocated_blocks;
|
||||||
|
Loading…
Reference in New Issue
Block a user