| From 2a7bf53f577e49c43de4ffa7776056de26db65d9 Mon Sep 17 00:00:00 2001 |
| From: Robbie Ko <robbieko@synology.com> |
| Date: Fri, 7 Oct 2016 17:30:47 +0800 |
| Subject: Btrfs: fix tree search logic when replaying directory entry deletes |
| |
| From: Robbie Ko <robbieko@synology.com> |
| |
| commit 2a7bf53f577e49c43de4ffa7776056de26db65d9 upstream. |
| |
| If a log tree has a layout like the following: |
| |
| leaf N: |
| ... |
| item 240 key (282 DIR_LOG_ITEM 0) itemoff 8189 itemsize 8 |
| dir log end 1275809046 |
| leaf N + 1: |
| item 0 key (282 DIR_LOG_ITEM 3936149215) itemoff 16275 itemsize 8 |
| dir log end 18446744073709551615 |
| ... |
| |
| When we pass the value 1275809046 + 1 as the parameter start_ret to the |
| function tree-log.c:find_dir_range() (done by replay_dir_deletes()), we |
| end up with path->slots[0] having the value 239 (points to the last item |
| of leaf N, item 240). Because the dir log item in that position has an |
| offset value smaller than *start_ret (1275809046 + 1) we need to move on |
| to the next leaf, however the logic for that is wrong since it compares |
| the current slot to the number of items in the leaf, which is smaller |
| and therefore we don't lookup for the next leaf but instead we set the |
| slot to point to an item that does not exist, at slot 240, and we later |
| operate on that slot which has unexpected content or in the worst case |
| can result in an invalid memory access (accessing beyond the last page |
| of leaf N's extent buffer). |
| |
| So fix the logic that checks when we need to lookup at the next leaf |
| by first incrementing the slot and only after to check if that slot |
| is beyond the last item of the current leaf. |
| |
| Signed-off-by: Robbie Ko <robbieko@synology.com> |
| Reviewed-by: Filipe Manana <fdmanana@suse.com> |
| Fixes: e02119d5a7b4 (Btrfs: Add a write ahead tree log to optimize synchronous operations) |
| Signed-off-by: Filipe Manana <fdmanana@suse.com> |
| [Modified changelog for clarity and correctness] |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/btrfs/tree-log.c | 3 +-- |
| 1 file changed, 1 insertion(+), 2 deletions(-) |
| |
| --- a/fs/btrfs/tree-log.c |
| +++ b/fs/btrfs/tree-log.c |
| @@ -1923,12 +1923,11 @@ static noinline int find_dir_range(struc |
| next: |
| /* check the next slot in the tree to see if it is a valid item */ |
| nritems = btrfs_header_nritems(path->nodes[0]); |
| + path->slots[0]++; |
| if (path->slots[0] >= nritems) { |
| ret = btrfs_next_leaf(root, path); |
| if (ret) |
| goto out; |
| - } else { |
| - path->slots[0]++; |
| } |
| |
| btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); |