From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 063532F83A2 for ; Thu, 19 Mar 2026 18:35:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773945317; cv=none; b=lfDdlg6EgEJaRoMXDnnmGI8h2el9S6e5tmXYZIwOcDMDNrbMOgbKuGM819y9sZEWUYPLdaN0wrS3va26ycjpWTXDDHafUsW8Bz9ZMHF1RmVt6fFf4jw9gHGgsFJ6qmpxoxHPC4/Za080Tfk3pUTG7q9ARYI4OUFWExyDWsmX6+0= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773945317; c=relaxed/simple; bh=a0/QsxplGDEwqTteE7Vj27NR9xXs1CcCgftKGGupo4o=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=nVgdZViJmmBpfuaCvOiSxQG2axQ2HrbYAkTwgIF4Iip6TjnBq4xcfvYRrBo7w+TSk+h3WKzcn4VV2qOH5HTdodW98MS1PC5rM/90CuD6yYn6F9VdI1mDn3zp1/zhzhoMuyuTEsF1rd4MalPImdzrKlHolJs0ngH1LxonDtbCd1w= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=KCaSmDtU; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="KCaSmDtU" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 257BBC2BC87; Thu, 19 Mar 2026 18:35:16 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1773945316; bh=a0/QsxplGDEwqTteE7Vj27NR9xXs1CcCgftKGGupo4o=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=KCaSmDtUARv+9yBFEaRtDGhEwdJzmnoB19EylMJnmasgigDUEw09PjEOM7xxvAsPf vfBTb13euWNdsnzJso4muLTUL9GOK09/OO9rjt8p4HYLzV5SFjeNHJCsfNqMrdfgUu /ORhtfGzhFBur6KjsJQZP4R692k++2Y0wBAyKvYHRCi93W1juFXdkdNze3EhH8nIr1 DQqQK9aTk0bZZeayTLzT5VUvsxQpKwXdwQGosSuZrSXBh0mz8b9mWVqETIFR1xuSib Fv0dTFbmGXr+2jnytNAXRTaHT/R9E8eq0z7Y1gR5wS4K5MVSGSufIs6QCA1DNad2Fs vggbhILcVwPNg== From: Sasha Levin To: stable@vger.kernel.org Cc: Filipe Manana , Anand Jain , David Sterba , Sasha Levin Subject: [PATCH 6.6.y] btrfs: fix transaction abort on set received ioctl due to item overflow Date: Thu, 19 Mar 2026 14:35:14 -0400 Message-ID: <20260319183514.2930118-1-sashal@kernel.org> X-Mailer: git-send-email 2.51.0 In-Reply-To: <2026031708-makeover-flaccid-62b2@gregkh> References: <2026031708-makeover-flaccid-62b2@gregkh> Precedence: bulk X-Mailing-List: stable@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: Filipe Manana [ Upstream commit 87f2c46003fce4d739138aab4af1942b1afdadac ] If the set received ioctl fails due to an item overflow when attempting to add the BTRFS_UUID_KEY_RECEIVED_SUBVOL we have to abort the transaction since we did some metadata updates before. This means that if a user calls this ioctl with the same received UUID field for a lot of subvolumes, we will hit the overflow, trigger the transaction abort and turn the filesystem into RO mode. A malicious user could exploit this, and this ioctl does not even requires that a user has admin privileges (CAP_SYS_ADMIN), only that he/she owns the subvolume. Fix this by doing an early check for item overflow before starting a transaction. This is also race safe because we are holding the subvol_sem semaphore in exclusive (write) mode. A test case for fstests will follow soon. Fixes: dd5f9615fc5c ("Btrfs: maintain subvolume items in the UUID tree") CC: stable@vger.kernel.org # 3.12+ Reviewed-by: Anand Jain Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba [ adapted BTRFS_PATH_AUTO_FREE macro to manual btrfs_free_path calls ] Signed-off-by: Sasha Levin --- fs/btrfs/ioctl.c | 21 +++++++++++++++++++-- fs/btrfs/uuid-tree.c | 43 +++++++++++++++++++++++++++++++++++++++++++ fs/btrfs/uuid-tree.h | 2 ++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 71e6715efa14c..03ea34790b5d2 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -3999,6 +3999,25 @@ static long _btrfs_ioctl_set_received_subvol(struct file *file, goto out; } + received_uuid_changed = memcmp(root_item->received_uuid, sa->uuid, + BTRFS_UUID_SIZE); + + /* + * Before we attempt to add the new received uuid, check if we have room + * for it in case there's already an item. If the size of the existing + * item plus this root's ID (u64) exceeds the maximum item size, we can + * return here without the need to abort a transaction. If we don't do + * this check, the btrfs_uuid_tree_add() call below would fail with + * -EOVERFLOW and result in a transaction abort. Malicious users could + * exploit this to turn the fs into RO mode. + */ + if (received_uuid_changed && !btrfs_is_empty_uuid(sa->uuid)) { + ret = btrfs_uuid_tree_check_overflow(fs_info, sa->uuid, + BTRFS_UUID_KEY_RECEIVED_SUBVOL); + if (ret < 0) + goto out; + } + /* * 1 - root item * 2 - uuid items (received uuid + subvol uuid) @@ -4014,8 +4033,6 @@ static long _btrfs_ioctl_set_received_subvol(struct file *file, sa->rtime.sec = ct.tv_sec; sa->rtime.nsec = ct.tv_nsec; - received_uuid_changed = memcmp(root_item->received_uuid, sa->uuid, - BTRFS_UUID_SIZE); if (received_uuid_changed && !btrfs_is_empty_uuid(root_item->received_uuid)) { ret = btrfs_uuid_tree_remove(trans, root_item->received_uuid, diff --git a/fs/btrfs/uuid-tree.c b/fs/btrfs/uuid-tree.c index 5be74f9e47ebf..0987fdcaea68c 100644 --- a/fs/btrfs/uuid-tree.c +++ b/fs/btrfs/uuid-tree.c @@ -228,6 +228,49 @@ int btrfs_uuid_tree_remove(struct btrfs_trans_handle *trans, u8 *uuid, u8 type, return ret; } +/* + * Check if we can add one root ID to a UUID key. + * If the key does not yet exists, we can, otherwise only if extended item does + * not exceeds the maximum item size permitted by the leaf size. + * + * Returns 0 on success, negative value on error. + */ +int btrfs_uuid_tree_check_overflow(struct btrfs_fs_info *fs_info, + u8 *uuid, u8 type) +{ + struct btrfs_path *path; + int ret; + u32 item_size; + struct btrfs_key key; + + if (WARN_ON_ONCE(!fs_info->uuid_root)) + return -EINVAL; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + btrfs_uuid_to_key(uuid, type, &key); + ret = btrfs_search_slot(NULL, fs_info->uuid_root, &key, path, 0, 0); + if (ret < 0) { + btrfs_free_path(path); + return ret; + } + if (ret > 0) { + btrfs_free_path(path); + return 0; + } + + item_size = btrfs_item_size(path->nodes[0], path->slots[0]); + btrfs_free_path(path); + + if (sizeof(struct btrfs_item) + item_size + sizeof(u64) > + BTRFS_LEAF_DATA_SIZE(fs_info)) + return -EOVERFLOW; + + return 0; +} + static int btrfs_uuid_iter_rem(struct btrfs_root *uuid_root, u8 *uuid, u8 type, u64 subid) { diff --git a/fs/btrfs/uuid-tree.h b/fs/btrfs/uuid-tree.h index 5350c87fe2caf..29e7b76c64621 100644 --- a/fs/btrfs/uuid-tree.h +++ b/fs/btrfs/uuid-tree.h @@ -7,6 +7,8 @@ int btrfs_uuid_tree_add(struct btrfs_trans_handle *trans, u8 *uuid, u8 type, u64 subid); int btrfs_uuid_tree_remove(struct btrfs_trans_handle *trans, u8 *uuid, u8 type, u64 subid); +int btrfs_uuid_tree_check_overflow(struct btrfs_fs_info *fs_info, + u8 *uuid, u8 type); int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info); #endif -- 2.51.0