From d1e62a43c932120b0d92ee40498f4c65d132da03 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 28 Jul 2020 16:32:59 -0700 Subject: [PATCH] scoutfs: fix leaking alloc bits in merge In a merge where the input and source trees are the same, the input block can be an initial pre-cow version of the dirty source block. Dirtying blocks in the change will clear allocations in the dirty source block but they will remain in the pre-cow input block. The merge can then set these blocks in the dst, even though they were also used by allocation, because they're still set in the pre-cow input block. This fix is clumsy, but minimal and specific to this problem. A more thorough fix is being worked on which introduces more staging more allocator trees and should stop calls that are modifying the current active avail or free trees. Signed-off-by: Zach Brown --- kmod/src/counters.h | 1 + kmod/src/radix.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/kmod/src/counters.h b/kmod/src/counters.h index d3e1b175..bce86418 100644 --- a/kmod/src/counters.h +++ b/kmod/src/counters.h @@ -135,6 +135,7 @@ EXPAND_COUNTER(radix_inconsistent_eio) \ EXPAND_COUNTER(radix_inconsistent_ref) \ EXPAND_COUNTER(radix_merge) \ + EXPAND_COUNTER(radix_merge_bad_clean_input) \ EXPAND_COUNTER(radix_merge_empty) \ EXPAND_COUNTER(radix_undo_ref) \ EXPAND_COUNTER(radix_walk) \ diff --git a/kmod/src/radix.c b/kmod/src/radix.c index 4f636bf7..0e734ff5 100644 --- a/kmod/src/radix.c +++ b/kmod/src/radix.c @@ -1392,6 +1392,21 @@ wrapped: goto out; src_rdx = src_bl->data; + /* + * If we're searching the avail allocator tree then we + * must be sure that we copy leaves after change + * allocations have been applied. If we had a read-only + * copy of the allocator leaf before it was cowed we + * could merge bits that were used for dirty block + * allocations by the change. By not resetting the + * change the repeated lookup will find the current + * dirty leaf block. + */ + if (src == inp && inp_bl != src_bl) { + scoutfs_inc_counter(sb, radix_merge_bad_clean_input); + goto wrapped; + } + ret = get_leaf(sb, alloc, wri, &chg, dst, GLF_DIRTY, bit, &leaf_bit, &dst_bl); if (ret < 0)