diff mbox series

[10/20] ext4: get rid of ppath in ext4_ext_create_new_leaf()

Message ID 20240710040654.1714672-11-libaokun@huaweicloud.com
State Superseded
Headers show
Series ext4: some bugfixes and cleanups for ext4 extents path | expand

Commit Message

Baokun Li July 10, 2024, 4:06 a.m. UTC
From: Baokun Li <libaokun1@huawei.com>

The use of path and ppath is now very confusing, so to make the code more
readable, pass path between functions uniformly, and get rid of ppath.

To get rid of the ppath in ext4_ext_create_new_leaf(), the following is
done here:

 * Free the extents path when an error is encountered.
 * Its caller needs to update ppath if it uses ppath.

No functional changes.

Signed-off-by: Baokun Li <libaokun1@huawei.com>
---
 fs/ext4/extents.c | 41 +++++++++++++++++++++--------------------
 1 file changed, 21 insertions(+), 20 deletions(-)

Comments

Jan Kara July 25, 2024, 10:46 a.m. UTC | #1
On Wed 10-07-24 12:06:44, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> To get rid of the ppath in ext4_ext_create_new_leaf(), the following is
> done here:
> 
>  * Free the extents path when an error is encountered.
>  * Its caller needs to update ppath if it uses ppath.
> 
> No functional changes.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Just one nit below. Otherwise feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

> @@ -1424,28 +1423,24 @@ static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
>  		 * entry: create all needed subtree and add new leaf */
>  		err = ext4_ext_split(handle, inode, mb_flags, path, newext, i);
>  		if (err)
> -			goto out;
> +			goto errout;
>  
>  		/* refill path */
>  		path = ext4_find_extent(inode,
>  				    (ext4_lblk_t)le32_to_cpu(newext->ee_block),
>  				    path, gb_flags);
> -		if (IS_ERR(path))
> -			err = PTR_ERR(path);

So I'd rather have here:
		return path;

That way it's obvious we will not confuse some code below with error stored
in 'path' and we can also save one indentation level by removing 'else'
below (probably do reindenting in a separate patch).

								Honza

>  	} else {
>  		/* tree is full, time to grow in depth */
>  		err = ext4_ext_grow_indepth(handle, inode, mb_flags);
>  		if (err)
> -			goto out;
> +			goto errout;
>  
>  		/* refill path */
>  		path = ext4_find_extent(inode,
>  				   (ext4_lblk_t)le32_to_cpu(newext->ee_block),
>  				    path, gb_flags);
> -		if (IS_ERR(path)) {
> -			err = PTR_ERR(path);
> -			goto out;
> -		}
> +		if (IS_ERR(path))
> +			return path;
>  
>  		/*
>  		 * only first (depth 0 -> 1) produces free space;
> @@ -1457,9 +1452,11 @@ static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
>  			goto repeat;
>  		}
>  	}
> -out:
> -	*ppath = IS_ERR(path) ? NULL : path;
> -	return err;
> +	return path;
> +
> +errout:
> +	ext4_free_ext_path(path);
> +	return ERR_PTR(err);
>  }
>  
>  /*
> @@ -2112,10 +2109,14 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
>  	 */
>  	if (gb_flags & EXT4_GET_BLOCKS_METADATA_NOFAIL)
>  		mb_flags |= EXT4_MB_USE_RESERVED;
> -	err = ext4_ext_create_new_leaf(handle, inode, mb_flags, gb_flags,
> -				       ppath, newext);
> -	if (err)
> +	path = ext4_ext_create_new_leaf(handle, inode, mb_flags, gb_flags,
> +					path, newext);
> +	if (IS_ERR(path)) {
> +		*ppath = NULL;
> +		err = PTR_ERR(path);
>  		goto cleanup;
> +	}
> +	*ppath = path;
>  	depth = ext_depth(inode);
>  	eh = path[depth].p_hdr;
>  
> -- 
> 2.39.2
>
Baokun Li July 27, 2024, 6:35 a.m. UTC | #2
On 2024/7/25 18:46, Jan Kara wrote:
> On Wed 10-07-24 12:06:44, libaokun@huaweicloud.com wrote:
>> From: Baokun Li <libaokun1@huawei.com>
>>
>> The use of path and ppath is now very confusing, so to make the code more
>> readable, pass path between functions uniformly, and get rid of ppath.
>>
>> To get rid of the ppath in ext4_ext_create_new_leaf(), the following is
>> done here:
>>
>>   * Free the extents path when an error is encountered.
>>   * Its caller needs to update ppath if it uses ppath.
>>
>> No functional changes.
>>
>> Signed-off-by: Baokun Li <libaokun1@huawei.com>
> Just one nit below. Otherwise feel free to add:
>
> Reviewed-by: Jan Kara <jack@suse.cz>
>
>> @@ -1424,28 +1423,24 @@ static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
>>   		 * entry: create all needed subtree and add new leaf */
>>   		err = ext4_ext_split(handle, inode, mb_flags, path, newext, i);
>>   		if (err)
>> -			goto out;
>> +			goto errout;
>>   
>>   		/* refill path */
>>   		path = ext4_find_extent(inode,
>>   				    (ext4_lblk_t)le32_to_cpu(newext->ee_block),
>>   				    path, gb_flags);
>> -		if (IS_ERR(path))
>> -			err = PTR_ERR(path);
> So I'd rather have here:
> 		return path;
>
> That way it's obvious we will not confuse some code below with error stored
> in 'path' and we can also save one indentation level by removing 'else'
> below (probably do reindenting in a separate patch).
>
> 								Honza
Yes, that looks clearer! I'll add 'return path' in the next version, and
add a cleanup patch after this one to save indentation.

Thanks,
Baokun
>>   	} else {
>>   		/* tree is full, time to grow in depth */
>>   		err = ext4_ext_grow_indepth(handle, inode, mb_flags);
>>   		if (err)
>> -			goto out;
>> +			goto errout;
>>   
>>   		/* refill path */
>>   		path = ext4_find_extent(inode,
>>   				   (ext4_lblk_t)le32_to_cpu(newext->ee_block),
>>   				    path, gb_flags);
>> -		if (IS_ERR(path)) {
>> -			err = PTR_ERR(path);
>> -			goto out;
>> -		}
>> +		if (IS_ERR(path))
>> +			return path;
>>   
>>   		/*
>>   		 * only first (depth 0 -> 1) produces free space;
>> @@ -1457,9 +1452,11 @@ static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
>>   			goto repeat;
>>   		}
>>   	}
>> -out:
>> -	*ppath = IS_ERR(path) ? NULL : path;
>> -	return err;
>> +	return path;
>> +
>> +errout:
>> +	ext4_free_ext_path(path);
>> +	return ERR_PTR(err);
>>   }
>>   
>>   /*
>> @@ -2112,10 +2109,14 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
>>   	 */
>>   	if (gb_flags & EXT4_GET_BLOCKS_METADATA_NOFAIL)
>>   		mb_flags |= EXT4_MB_USE_RESERVED;
>> -	err = ext4_ext_create_new_leaf(handle, inode, mb_flags, gb_flags,
>> -				       ppath, newext);
>> -	if (err)
>> +	path = ext4_ext_create_new_leaf(handle, inode, mb_flags, gb_flags,
>> +					path, newext);
>> +	if (IS_ERR(path)) {
>> +		*ppath = NULL;
>> +		err = PTR_ERR(path);
>>   		goto cleanup;
>> +	}
>> +	*ppath = path;
>>   	depth = ext_depth(inode);
>>   	eh = path[depth].p_hdr;
>>   
>> -- 
>> 2.39.2
>>
Ojaswin Mujoo Aug. 2, 2024, 7:34 a.m. UTC | #3
On Wed, Jul 10, 2024 at 12:06:44PM +0800, libaokun@huaweicloud.com wrote:
> From: Baokun Li <libaokun1@huawei.com>
> 
> The use of path and ppath is now very confusing, so to make the code more
> readable, pass path between functions uniformly, and get rid of ppath.
> 
> To get rid of the ppath in ext4_ext_create_new_leaf(), the following is
> done here:
> 
>  * Free the extents path when an error is encountered.
>  * Its caller needs to update ppath if it uses ppath.
> 
> No functional changes.
> 
> Signed-off-by: Baokun Li <libaokun1@huawei.com>

Hi Baokun,

The changes look good to me, feel free to add:

Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>

That being said, IIUC i think this patchset also fixes a potential UAF
bug. Below is a sample trace with dummy values:

ext4_ext_insert_extent
  path = *ppath = 2000
  ext4_ext_create_new_leaf(ppath)
    path = *ppath = 2000
    ext4_find_extent(path = 2000)
      if (depth > path[0].p_maxdepth)
            kfree(path = 2000);
            path = NULL;
      path = kcalloc() = 3000
      ...
      return path;
  path = 3000
  *ppath = 3000;
  return;
/* here path is still 2000 *, UAF! */
eh = path[depth].p_hdr 

I'm not completely sure if we can hit (depth > path[0].p_maxdepth) in the
above codepath but I think the flow is still a bit fragile. Maybe this
should be fixed in a separate patch first. What do you think?

Regards,
ojaswin

 ---
>  fs/ext4/extents.c | 41 +++++++++++++++++++++--------------------
>  1 file changed, 21 insertions(+), 20 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 6dfb5d03e197..0d6ce9e74b01 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -1397,13 +1397,12 @@ static int ext4_ext_grow_indepth(handle_t *handle, struct inode *inode,
>   * finds empty index and adds new leaf.
>   * if no free index is found, then it requests in-depth growing.
>   */
> -static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
> -           unsigned int mb_flags,
> -           unsigned int gb_flags,
> -           struct ext4_ext_path **ppath,
> -           struct ext4_extent *newext)
> +static struct ext4_ext_path *
> +ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
> +      unsigned int mb_flags, unsigned int gb_flags,
> +      struct ext4_ext_path *path,
> +      struct ext4_extent *newext)
>  {
> - struct ext4_ext_path *path = *ppath;
>   struct ext4_ext_path *curp;
>   int depth, i, err = 0;
>  
> @@ -1424,28 +1423,24 @@ static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
>      * entry: create all needed subtree and add new leaf */
>     err = ext4_ext_split(handle, inode, mb_flags, path, newext, i);
>     if (err)
> -     goto out;
> +     goto errout;
>  
>     /* refill path */
>     path = ext4_find_extent(inode,
>             (ext4_lblk_t)le32_to_cpu(newext->ee_block),
>             path, gb_flags);
> -   if (IS_ERR(path))
> -     err = PTR_ERR(path);
>   } else {
>     /* tree is full, time to grow in depth */
>     err = ext4_ext_grow_indepth(handle, inode, mb_flags);
>     if (err)
> -     goto out;
> +     goto errout;
>  
>     /* refill path */
>     path = ext4_find_extent(inode,
>            (ext4_lblk_t)le32_to_cpu(newext->ee_block),
>             path, gb_flags);
> -   if (IS_ERR(path)) {
> -     err = PTR_ERR(path);
> -     goto out;
> -   }
> +   if (IS_ERR(path))
> +     return path;
>  
>     /*
>      * only first (depth 0 -> 1) produces free space;
> @@ -1457,9 +1452,11 @@ static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
>       goto repeat;
>     }
>   }
> -out:
> - *ppath = IS_ERR(path) ? NULL : path;
> - return err;
> + return path;
> +
> +errout:
> + ext4_free_ext_path(path);
> + return ERR_PTR(err);
>  }
>  
>  /*
> @@ -2112,10 +2109,14 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
>    */
>   if (gb_flags & EXT4_GET_BLOCKS_METADATA_NOFAIL)
>     mb_flags |= EXT4_MB_USE_RESERVED;
> - err = ext4_ext_create_new_leaf(handle, inode, mb_flags, gb_flags,
> -              ppath, newext);
> - if (err)
> + path = ext4_ext_create_new_leaf(handle, inode, mb_flags, gb_flags,
> +         path, newext);
> + if (IS_ERR(path)) {
> +   *ppath = NULL;
> +   err = PTR_ERR(path);
>     goto cleanup;
> + }
> + *ppath = path;
>   depth = ext_depth(inode);
>   eh = path[depth].p_hdr;
>  
> -- 
> 2.39.2
>
Baokun Li Aug. 2, 2024, 1:07 p.m. UTC | #4
On 2024/8/2 15:34, Ojaswin Mujoo wrote:
> On Wed, Jul 10, 2024 at 12:06:44PM +0800, libaokun@huaweicloud.com wrote:
>> From: Baokun Li <libaokun1@huawei.com>
>>
>> The use of path and ppath is now very confusing, so to make the code more
>> readable, pass path between functions uniformly, and get rid of ppath.
>>
>> To get rid of the ppath in ext4_ext_create_new_leaf(), the following is
>> done here:
>>
>>   * Free the extents path when an error is encountered.
>>   * Its caller needs to update ppath if it uses ppath.
>>
>> No functional changes.
>>
>> Signed-off-by: Baokun Li <libaokun1@huawei.com>
> Hi Baokun,
Hey Ojaswin,
> The changes look good to me, feel free to add:
>
> Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
Thank you very much for your review!
>
> That being said, IIUC i think this patchset also fixes a potential UAF
> bug. Below is a sample trace with dummy values:
>
> ext4_ext_insert_extent
>    path = *ppath = 2000
>    ext4_ext_create_new_leaf(ppath)
>      path = *ppath = 2000
>      ext4_find_extent(path = 2000)
>        if (depth > path[0].p_maxdepth)
>              kfree(path = 2000);
>              path = NULL;
>        path = kcalloc() = 3000
>        ...
>        return path;
>    path = 3000
>    *ppath = 3000;
>    return;
> /* here path is still 2000 *, UAF! */
> eh = path[depth].p_hdr
>
> I'm not completely sure if we can hit (depth > path[0].p_maxdepth) in the
> above codepath but I think the flow is still a bit fragile. Maybe this
> should be fixed in a separate patch first. What do you think?
>
> Regards,
> ojaswin
Nice catch!

This is indeed a potential UAF issue, and while it seems hard to
trigger (depth > path[0].p_maxdepth), it does deserve a separate
patch, and I'll be adding a separate quick fix for this in the next version.

Regards,
Baokun
diff mbox series

Patch

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 6dfb5d03e197..0d6ce9e74b01 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -1397,13 +1397,12 @@  static int ext4_ext_grow_indepth(handle_t *handle, struct inode *inode,
  * finds empty index and adds new leaf.
  * if no free index is found, then it requests in-depth growing.
  */
-static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
-				    unsigned int mb_flags,
-				    unsigned int gb_flags,
-				    struct ext4_ext_path **ppath,
-				    struct ext4_extent *newext)
+static struct ext4_ext_path *
+ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
+			 unsigned int mb_flags, unsigned int gb_flags,
+			 struct ext4_ext_path *path,
+			 struct ext4_extent *newext)
 {
-	struct ext4_ext_path *path = *ppath;
 	struct ext4_ext_path *curp;
 	int depth, i, err = 0;
 
@@ -1424,28 +1423,24 @@  static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
 		 * entry: create all needed subtree and add new leaf */
 		err = ext4_ext_split(handle, inode, mb_flags, path, newext, i);
 		if (err)
-			goto out;
+			goto errout;
 
 		/* refill path */
 		path = ext4_find_extent(inode,
 				    (ext4_lblk_t)le32_to_cpu(newext->ee_block),
 				    path, gb_flags);
-		if (IS_ERR(path))
-			err = PTR_ERR(path);
 	} else {
 		/* tree is full, time to grow in depth */
 		err = ext4_ext_grow_indepth(handle, inode, mb_flags);
 		if (err)
-			goto out;
+			goto errout;
 
 		/* refill path */
 		path = ext4_find_extent(inode,
 				   (ext4_lblk_t)le32_to_cpu(newext->ee_block),
 				    path, gb_flags);
-		if (IS_ERR(path)) {
-			err = PTR_ERR(path);
-			goto out;
-		}
+		if (IS_ERR(path))
+			return path;
 
 		/*
 		 * only first (depth 0 -> 1) produces free space;
@@ -1457,9 +1452,11 @@  static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
 			goto repeat;
 		}
 	}
-out:
-	*ppath = IS_ERR(path) ? NULL : path;
-	return err;
+	return path;
+
+errout:
+	ext4_free_ext_path(path);
+	return ERR_PTR(err);
 }
 
 /*
@@ -2112,10 +2109,14 @@  int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
 	 */
 	if (gb_flags & EXT4_GET_BLOCKS_METADATA_NOFAIL)
 		mb_flags |= EXT4_MB_USE_RESERVED;
-	err = ext4_ext_create_new_leaf(handle, inode, mb_flags, gb_flags,
-				       ppath, newext);
-	if (err)
+	path = ext4_ext_create_new_leaf(handle, inode, mb_flags, gb_flags,
+					path, newext);
+	if (IS_ERR(path)) {
+		*ppath = NULL;
+		err = PTR_ERR(path);
 		goto cleanup;
+	}
+	*ppath = path;
 	depth = ext_depth(inode);
 	eh = path[depth].p_hdr;