mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-13 10:02:38 +00:00
- Fix radix tree memory leakage when unloading modules using radix
trees. This happens because the logic inserting items into the radix tree is allocating empty radix levels, when index zero does not contain any items. - Add proper error case handling, so that the radix tree does not end up in a bad state, if memory cannot be allocated during insertion of an item. - Add check for inserting NULL items into the radix tree. - Add check for radix tree getting too big. MFC after: 1 week Sponsored by: Mellanox Technologies
This commit is contained in:
parent
c79bec9c75
commit
918ba0175b
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=269859
@ -123,40 +123,84 @@ int
|
||||
radix_tree_insert(struct radix_tree_root *root, unsigned long index, void *item)
|
||||
{
|
||||
struct radix_tree_node *node;
|
||||
struct radix_tree_node *temp[RADIX_TREE_MAX_HEIGHT - 1];
|
||||
int height;
|
||||
int idx;
|
||||
|
||||
/* bail out upon insertion of a NULL item */
|
||||
if (item == NULL)
|
||||
return (-EINVAL);
|
||||
|
||||
/* get root node, if any */
|
||||
node = root->rnode;
|
||||
|
||||
/* allocate root node, if any */
|
||||
if (node == NULL) {
|
||||
node = malloc(sizeof(*node), M_RADIX, root->gfp_mask | M_ZERO);
|
||||
if (node == NULL)
|
||||
return (-ENOMEM);
|
||||
root->rnode = node;
|
||||
root->height++;
|
||||
}
|
||||
|
||||
/* expand radix tree as needed */
|
||||
while (radix_max(root) < index) {
|
||||
|
||||
/* check if the radix tree is getting too big */
|
||||
if (root->height == RADIX_TREE_MAX_HEIGHT)
|
||||
return (-E2BIG);
|
||||
|
||||
/*
|
||||
* Expand the tree to fit indexes as big as requested.
|
||||
* If the root radix level is not empty, we need to
|
||||
* allocate a new radix level:
|
||||
*/
|
||||
while (root->rnode == NULL || radix_max(root) < index) {
|
||||
if (node->count != 0) {
|
||||
node = malloc(sizeof(*node), M_RADIX, root->gfp_mask | M_ZERO);
|
||||
if (node == NULL)
|
||||
return (-ENOMEM);
|
||||
node->slots[0] = root->rnode;
|
||||
if (root->rnode)
|
||||
node->count++;
|
||||
root->rnode = node;
|
||||
}
|
||||
root->height++;
|
||||
}
|
||||
node = root->rnode;
|
||||
|
||||
/* get radix tree height index */
|
||||
height = root->height - 1;
|
||||
/*
|
||||
* Walk down the tree finding the correct node and allocating any
|
||||
* missing nodes along the way.
|
||||
*/
|
||||
while (height) {
|
||||
|
||||
/* walk down the tree until the first missing node, if any */
|
||||
for ( ; height != 0; height--) {
|
||||
idx = radix_pos(index, height);
|
||||
if (node->slots[idx] == NULL) {
|
||||
node->slots[idx] = malloc(sizeof(*node), M_RADIX,
|
||||
root->gfp_mask | M_ZERO);
|
||||
if (node->slots[idx] == NULL)
|
||||
return (-ENOMEM);
|
||||
node->count++;
|
||||
}
|
||||
break;
|
||||
node = node->slots[idx];
|
||||
height--;
|
||||
}
|
||||
|
||||
/* allocate the missing radix levels, if any */
|
||||
for (idx = 0; idx != height; idx++) {
|
||||
temp[idx] = malloc(sizeof(*node), M_RADIX,
|
||||
root->gfp_mask | M_ZERO);
|
||||
if (temp[idx] == NULL) {
|
||||
while(idx--)
|
||||
free(temp[idx], M_RADIX);
|
||||
/* check if we should free the root node aswell */
|
||||
if (root->rnode->count == 0) {
|
||||
free(root->rnode, M_RADIX);
|
||||
root->rnode = NULL;
|
||||
root->height = 0;
|
||||
}
|
||||
return (-ENOMEM);
|
||||
}
|
||||
}
|
||||
|
||||
/* setup new radix levels, if any */
|
||||
for ( ; height != 0; height--) {
|
||||
idx = radix_pos(index, height);
|
||||
node->slots[idx] = temp[height - 1];
|
||||
node->count++;
|
||||
node = node->slots[idx];
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert and adjust count if the item does not already exist.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user