diff mbox series

btrfs: fix use-after-free on head ref when adding delayed ref

Message ID 02fc507b62b19be2348fc08de8b13bd7af1a440e.1728922973.git.fdmanana@suse.com (mailing list archive)
State New
Headers show
Series btrfs: fix use-after-free on head ref when adding delayed ref | expand

Commit Message

Filipe Manana Oct. 14, 2024, 4:24 p.m. UTC
From: Filipe Manana <fdmanana@suse.com>

At add_delayed_ref(), if we added a qgroup record, we can trigger a
use-after-free on the head reference when calling
btrfs_qgroup_trace_extent_post() since we are not holding the delayed refs
spinlock anymore at that point and in the meanwhile other task may have
removed the head reference after running delayed refs.

So fix this by extracting the bytenr from the generic reference instead
of the head reference.

Reported-by: syzbot+c3a3a153f0190dca5be9@syzkaller.appspotmail.com
Link: https://lore.kernel.org/linux-btrfs/670d3f2c.050a0220.3e960.0066.GAE@google.com/
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---

This is meant to be squashed into the following patch that is in for-next
but not in Linus' tree:

  "btrfs: qgroups: remove bytenr field from struct btrfs_qgroup_extent_record"

 fs/btrfs/delayed-ref.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Comments

Boris Burkov Oct. 14, 2024, 5 p.m. UTC | #1
On Mon, Oct 14, 2024 at 05:24:26PM +0100, fdmanana@kernel.org wrote:
> From: Filipe Manana <fdmanana@suse.com>
> 
> At add_delayed_ref(), if we added a qgroup record, we can trigger a
> use-after-free on the head reference when calling
> btrfs_qgroup_trace_extent_post() since we are not holding the delayed refs
> spinlock anymore at that point and in the meanwhile other task may have
> removed the head reference after running delayed refs.
> 
> So fix this by extracting the bytenr from the generic reference instead
> of the head reference.
> 
> Reported-by: syzbot+c3a3a153f0190dca5be9@syzkaller.appspotmail.com
> Link: https://lore.kernel.org/linux-btrfs/670d3f2c.050a0220.3e960.0066.GAE@google.com/
> Signed-off-by: Filipe Manana <fdmanana@suse.com>

Reviewed-by: Boris Burkov <boris@bur.io>

> ---
> 
> This is meant to be squashed into the following patch that is in for-next
> but not in Linus' tree:
> 
>   "btrfs: qgroups: remove bytenr field from struct btrfs_qgroup_extent_record"
> 
>  fs/btrfs/delayed-ref.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c
> index 13c2e00d1270..04586aa11917 100644
> --- a/fs/btrfs/delayed-ref.c
> +++ b/fs/btrfs/delayed-ref.c
> @@ -1074,7 +1074,7 @@ static int add_delayed_ref(struct btrfs_trans_handle *trans,
>  		kmem_cache_free(btrfs_delayed_ref_node_cachep, node);
>  
>  	if (qrecord_inserted)
> -		return btrfs_qgroup_trace_extent_post(trans, record, head_ref->bytenr);
> +		return btrfs_qgroup_trace_extent_post(trans, record, generic_ref->bytenr);
>  	return 0;
>  
>  free_record:
> -- 
> 2.43.0
>
Qu Wenruo Oct. 14, 2024, 8:56 p.m. UTC | #2
在 2024/10/15 02:54, fdmanana@kernel.org 写道:
> From: Filipe Manana <fdmanana@suse.com>
>
> At add_delayed_ref(), if we added a qgroup record, we can trigger a
> use-after-free on the head reference when calling
> btrfs_qgroup_trace_extent_post() since we are not holding the delayed refs
> spinlock anymore at that point and in the meanwhile other task may have
> removed the head reference after running delayed refs.
>
> So fix this by extracting the bytenr from the generic reference instead
> of the head reference.
>
> Reported-by: syzbot+c3a3a153f0190dca5be9@syzkaller.appspotmail.com
> Link: https://lore.kernel.org/linux-btrfs/670d3f2c.050a0220.3e960.0066.GAE@google.com/
> Signed-off-by: Filipe Manana <fdmanana@suse.com>

Reviewed-by: Qu Wenruo <wqu@suse.com>

Thanks,
Qu
> ---
>
> This is meant to be squashed into the following patch that is in for-next
> but not in Linus' tree:
>
>    "btrfs: qgroups: remove bytenr field from struct btrfs_qgroup_extent_record"
>
>   fs/btrfs/delayed-ref.c | 2 +-
>   1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c
> index 13c2e00d1270..04586aa11917 100644
> --- a/fs/btrfs/delayed-ref.c
> +++ b/fs/btrfs/delayed-ref.c
> @@ -1074,7 +1074,7 @@ static int add_delayed_ref(struct btrfs_trans_handle *trans,
>   		kmem_cache_free(btrfs_delayed_ref_node_cachep, node);
>
>   	if (qrecord_inserted)
> -		return btrfs_qgroup_trace_extent_post(trans, record, head_ref->bytenr);
> +		return btrfs_qgroup_trace_extent_post(trans, record, generic_ref->bytenr);
>   	return 0;
>
>   free_record:
David Sterba Oct. 15, 2024, 4:21 p.m. UTC | #3
On Mon, Oct 14, 2024 at 05:24:26PM +0100, fdmanana@kernel.org wrote:
> From: Filipe Manana <fdmanana@suse.com>
> 
> At add_delayed_ref(), if we added a qgroup record, we can trigger a
> use-after-free on the head reference when calling
> btrfs_qgroup_trace_extent_post() since we are not holding the delayed refs
> spinlock anymore at that point and in the meanwhile other task may have
> removed the head reference after running delayed refs.
> 
> So fix this by extracting the bytenr from the generic reference instead
> of the head reference.
> 
> Reported-by: syzbot+c3a3a153f0190dca5be9@syzkaller.appspotmail.com
> Link: https://lore.kernel.org/linux-btrfs/670d3f2c.050a0220.3e960.0066.GAE@google.com/
> Signed-off-by: Filipe Manana <fdmanana@suse.com>
> ---
> 
> This is meant to be squashed into the following patch that is in for-next
> but not in Linus' tree:
> 
>   "btrfs: qgroups: remove bytenr field from struct btrfs_qgroup_extent_record"

Thanks, I've updated the for-next branches and now it should be all
sorted.
diff mbox series

Patch

diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c
index 13c2e00d1270..04586aa11917 100644
--- a/fs/btrfs/delayed-ref.c
+++ b/fs/btrfs/delayed-ref.c
@@ -1074,7 +1074,7 @@  static int add_delayed_ref(struct btrfs_trans_handle *trans,
 		kmem_cache_free(btrfs_delayed_ref_node_cachep, node);
 
 	if (qrecord_inserted)
-		return btrfs_qgroup_trace_extent_post(trans, record, head_ref->bytenr);
+		return btrfs_qgroup_trace_extent_post(trans, record, generic_ref->bytenr);
 	return 0;
 
 free_record: