blist: Correct the node count computed in blist_create()

Commit bb4a27f927 added the ability to allocate a span of blocks
crossing a meta node boundary.  To ensure that blst_next_leaf_alloc()
does not walk past the end of the tree, an extra all-zero meta node
needs to be present at the end of the allocation, and
blst_next_leaf_alloc() is implemented such that the presence of this
node terminates the search.

blist_create() computes the number of nodes required.  It had two
problems:
1. When the size of the blist is a power of BLIST_RADIX, we would
   unnecessarily allocate an extra level in the tree.
2. When the size of the blist is a multiple of BLIST_RADIX, we would
   fail to allocate a terminator node.  In this case,
   blst_next_leaf_alloc() could scan beyond the bounds of the
   allocation.  This was found using KASAN.

Modify blist_create() to handle these cases correctly.

Reported by:	pho
Reviewed by:	dougm

(cherry picked from commit 2783335cae)
This commit is contained in:
Mark Johnston 2021-07-13 17:47:27 -04:00
parent a938bfca7a
commit f065d0bb29

View File

@ -244,8 +244,16 @@ blist_create(daddr_t blocks, int flags)
* Calculate the radix and node count used for scanning.
*/
nodes = 1;
for (radix = 1; radix <= blocks / BLIST_RADIX; radix *= BLIST_RADIX)
nodes += 1 + (blocks - 1) / radix / BLIST_RADIX;
for (radix = 1; (blocks - 1) / BLIST_RADIX / radix > 0;
radix *= BLIST_RADIX)
nodes += 1 + (blocks - 1) / BLIST_RADIX / radix;
/*
* Include a sentinel node to ensure that cross-leaf scans stay within
* the bounds of the allocation.
*/
if (blocks % BLIST_RADIX == 0)
nodes++;
bl = malloc(offsetof(struct blist, bl_root[nodes]), M_SWAP, flags |
M_ZERO);