FAILED: patch "[PATCH] Btrfs: fix use-after-free on root->orphan_block_rsv" failed to apply to 4.9-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 4.9-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
Quoted 1 line
From
1a932ef4e47984dee227834667b5ff5a334e4805Mon Sep 17 00:00:00 2001
From: Liu Bo <[email protected]>
Date: Thu, 25 Jan 2018 11:02:54 -0700
Subject: [PATCH] Btrfs: fix use-after-free on root->orphan_block_rsv
I got these from running generic/475,
WARNING: CPU: 0 PID: 26384 at fs/btrfs/inode.c:3326 btrfs_orphan_commit_root+0x1ac/0x2b0 [btrfs] BUG: unable to handle kernel NULL pointer dereference at 0000000000000010 IP: btrfs_block_rsv_release+0x1c/0x70 [btrfs] Call Trace: btrfs_orphan_release_metadata+0x9f/0x200 [btrfs] btrfs_orphan_del+0x10d/0x170 [btrfs] btrfs_setattr+0x500/0x640 [btrfs] notify_change+0x7ae/0x870 do_truncate+0xca/0x130 vfs_truncate+0x2ee/0x3d0 do_sys_truncate+0xaf/0xf0 SyS_truncate+0xe/0x10 entry_SYSCALL_64_fastpath+0x1f/0x96
The race is between btrfs_orphan_commit_root and btrfs_orphan_del,
t1 t2btrfs_orphan_commit_root btrfs_orphan_del
spin_lock
check (&root->orphan_inodes)
root->orphan_block_rsv = NULL;
spin_unlock
atomic_dec(&root->orphan_inodes);
access root->orphan_block_rsvAccessing root->orphan_block_rsv must be done before decreasing root->orphan_inodes.
fs/btrfs/inode.c +21 -13
@@ -3387,6 +3387,11 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans, ret =btrfs_orphan_reserve_metadata (trans, inode);ASSERT (!ret);if (ret) { +/* +* dec doesn't need spin_lock as ->orphan_block_rsv +* would be released only if ->orphan_inodes is +* zero. +*/ atomic_dec (&root->orphan_inodes);clear_bit (BTRFS_INODE_ORPHAN_META_RESERVED , &inode->runtime_flags);
@@ -3401,12 +3406,17 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans,if (insert >=1 ) { ret =btrfs_insert_orphan_item (trans, root,btrfs_ino (inode));if (ret) { -atomic_dec (&root->orphan_inodes);if (reserve) {clear_bit (BTRFS_INODE_ORPHAN_META_RESERVED , &inode->runtime_flags);btrfs_orphan_release_metadata (inode); } +/* +* btrfs_orphan_commit_root may race with us and set +* ->orphan_block_rsv to zero, in order to avoid that, +* decrease ->orphan_inodes after everything is done. +*/ +atomic_dec (&root->orphan_inodes);if (ret != -EEXIST ) {clear_bit (BTRFS_INODE_HAS_ORPHAN_ITEM , &inode->runtime_flags);
@@ -3438,28 +3448,26 @@ static int btrfs_orphan_del(struct btrfs_trans_handle *trans, {struct btrfs_root *root = inode->root;int delete_item =0 ; -int release_rsv =0 ;int ret =0 ; -spin_lock (&root->orphan_lock);if (test_and_clear_bit (BTRFS_INODE_HAS_ORPHAN_ITEM , &inode->runtime_flags)) delete_item =1 ; +if (delete_item && trans) + ret =btrfs_del_orphan_item (trans, root,btrfs_ino (inode)); +if (test_and_clear_bit (BTRFS_INODE_ORPHAN_META_RESERVED , &inode->runtime_flags)) - release_rsv =1 ; -spin_unlock (&root->orphan_lock); +btrfs_orphan_release_metadata (inode); -if (delete_item) { +/* +* btrfs_orphan_commit_root may race with us and set ->orphan_block_rsv +* to zero, in order to avoid that, decrease ->orphan_inodes after +* everything is done. +*/ +if (delete_item)atomic_dec (&root->orphan_inodes); -if (trans) - ret =btrfs_del_orphan_item (trans, root, -btrfs_ino (inode)); - } - -if (release_rsv) -btrfs_orphan_release_metadata (inode);return ret; }