]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
ext4: do not use extent after put_bh
authoryangerkun <yangerkun@huawei.com>
Wed, 28 Oct 2020 05:56:17 +0000 (13:56 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 5 Nov 2020 10:51:52 +0000 (11:51 +0100)
commit d7dce9e08595e80bf8039a81794809c66fe26431 upstream.

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
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/ext4/extents.c

index 3998c11f9517187eb7d094b611afac858736c1d5..9e506129ea367b6fc05926e69b57788af9bf7daa 100644 (file)
@@ -1472,16 +1472,16 @@ static int ext4_ext_search_left(struct inode *inode,
 }
 
 /*
- * search the closest allocated block to the right for *logical
- * and returns it at @logical + it's physical address at @phys
- * if *logical is the largest allocated block, the function
- * returns 0 at @phys
- * return value contains 0 (success) or error code
+ * Search the closest allocated block to the right for *logical
+ * and returns it at @logical + it's physical address at @phys.
+ * If not exists, return 0 and @phys is set to 0. We will return
+ * 1 which means we found an allocated block and ret_ex is valid.
+ * Or return a (< 0) error code.
  */
 static int ext4_ext_search_right(struct inode *inode,
                                 struct ext4_ext_path *path,
                                 ext4_lblk_t *logical, ext4_fsblk_t *phys,
-                                struct ext4_extent **ret_ex)
+                                struct ext4_extent *ret_ex)
 {
        struct buffer_head *bh = NULL;
        struct ext4_extent_header *eh;
@@ -1575,10 +1575,11 @@ got_index:
 found_extent:
        *logical = le32_to_cpu(ex->ee_block);
        *phys = ext4_ext_pblock(ex);
-       *ret_ex = ex;
+       if (ret_ex)
+               *ret_ex = *ex;
        if (bh)
                put_bh(bh);
-       return 0;
+       return 1;
 }
 
 /*
@@ -2869,8 +2870,8 @@ again:
                         */
                        lblk = ex_end + 1;
                        err = ext4_ext_search_right(inode, path, &lblk, &pblk,
-                                                   &ex);
-                       if (err)
+                                                   NULL);
+                       if (err < 0)
                                goto out;
                        if (pblk) {
                                partial.pclu = EXT4_B2C(sbi, pblk);
@@ -4038,7 +4039,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
                        struct ext4_map_blocks *map, int flags)
 {
        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);
        ext4_fsblk_t newblock = 0, pblk;
        int err = 0, depth, ret;
@@ -4174,15 +4175,14 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
        if (err)
                goto out;
        ar.lright = map->m_lblk;
-       ex2 = NULL;
        err = ext4_ext_search_right(inode, path, &ar.lright, &ar.pright, &ex2);
-       if (err)
+       if (err < 0)
                goto out;
 
        /* Check if the extent after searching to the right implies a
         * cluster we can use. */
-       if ((sbi->s_cluster_ratio > 1) && ex2 &&
-           get_implied_cluster_alloc(inode->i_sb, map, ex2, path)) {
+       if ((sbi->s_cluster_ratio > 1) && err &&
+           get_implied_cluster_alloc(inode->i_sb, map, &ex2, path)) {
                ar.len = allocated = map->m_len;
                newblock = map->m_pblk;
                goto got_allocated_blocks;