Re: [syzbot] [arm-msm?] [net?] memory leak in radix_tree_insert

From: syzbot
Date: Sun Dec 10 2023 - 23:41:17 EST


For archival purposes, forwarding an incoming command email to
linux-kernel@xxxxxxxxxxxxxxx.

***

Subject: [arm-msm?] [net?] memory leak in radix_tree_insert
Author: lizhi.xu@xxxxxxxxxxxxx

#syz test https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 33cc938e65a9

diff --git a/lib/radix-tree.c b/lib/radix-tree.c
index a89df8afa510..0a2dfecdcd30 100644
--- a/lib/radix-tree.c
+++ b/lib/radix-tree.c
@@ -613,12 +613,13 @@ static int __radix_tree_create(struct radix_tree_root *root,
unsigned long index, struct radix_tree_node **nodep,
void __rcu ***slotp)
{
- struct radix_tree_node *node = NULL, *child;
+ struct radix_tree_node *node = NULL, *child, *orig;
void __rcu **slot = (void __rcu **)&root->xa_head;
unsigned long maxindex;
- unsigned int shift, offset = 0;
+ unsigned int shift, offset = 0, mmshift = 0;
unsigned long max = index;
gfp_t gfp = root_gfp_mask(root);
+ int ret;

shift = radix_tree_load_root(root, &child, &maxindex);

@@ -628,7 +629,9 @@ static int __radix_tree_create(struct radix_tree_root *root,
if (error < 0)
return error;
shift = error;
+ mmshift = error;
child = rcu_dereference_raw(root->xa_head);
+ orig = child;
}

while (shift > 0) {
@@ -637,8 +640,11 @@ static int __radix_tree_create(struct radix_tree_root *root,
/* Have to add a child node. */
child = radix_tree_node_alloc(gfp, node, root, shift,
offset, 0, 0);
- if (!child)
- return -ENOMEM;
+ printk("nc: %p\n", child);
+ if (!child) {
+ ret = -ENOMEM;
+ goto freec;
+ }
rcu_assign_pointer(*slot, node_to_entry(child));
if (node)
node->count++;
@@ -656,6 +662,18 @@ static int __radix_tree_create(struct radix_tree_root *root,
if (slotp)
*slotp = slot;
return 0;
+freec:
+ if (mmshift > 0) {
+ struct radix_tree_node *dn;
+ while (shift < mmshift) {
+ radix_tree_descend(orig, &dn, index);
+ printk("dc: %p\n", dn);
+ orig = orig->slots[0];
+ radix_tree_node_rcu_free(&dn->rcu_head);
+ shift += RADIX_TREE_MAP_SHIFT;
+ }
+ }
+ return ret;
}

/*