[Syz report] BUG: memory leak unreferenced object 0xffff88810bbf56d8 (size 576): comm "syz-executor250", pid 5051, jiffies 4294951219 (age 12.920s) hex dump (first 32 bytes): 3c 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 <............... f0 a9 2d 0c 81 88 ff ff f0 56 bf 0b 81 88 ff ff ..-......V...... backtrace: [<ffffffff81631398>] kmemleak_alloc_recursive include/linux/kmemleak.h:42 [inline] [<ffffffff81631398>] slab_post_alloc_hook mm/slab.h:766 [inline] [<ffffffff81631398>] slab_alloc_node mm/slub.c:3478 [inline] [<ffffffff81631398>] slab_alloc mm/slub.c:3486 [inline] [<ffffffff81631398>] __kmem_cache_alloc_lru mm/slub.c:3493 [inline] [<ffffffff81631398>] kmem_cache_alloc+0x298/0x430 mm/slub.c:3502 [<ffffffff84b5094c>] radix_tree_node_alloc.constprop.0+0x7c/0x1a0 lib/radix-tree.c:276 [<ffffffff84b524cf>] __radix_tree_create lib/radix-tree.c:624 [inline] [<ffffffff84b524cf>] radix_tree_insert+0x14f/0x360 lib/radix-tree.c:712 [<ffffffff84ae105d>] qrtr_tx_wait net/qrtr/af_qrtr.c:277 [inline] [<ffffffff84ae105d>] qrtr_node_enqueue+0x57d/0x630 net/qrtr/af_qrtr.c:348 [<ffffffff84ae26f6>] qrtr_bcast_enqueue+0x66/0xd0 net/qrtr/af_qrtr.c:891 [<ffffffff84ae32d2>] qrtr_sendmsg+0x232/0x450 net/qrtr/af_qrtr.c:992 [<ffffffff83ec3c32>] sock_sendmsg_nosec net/socket.c:730 [inline] [<ffffffff83ec3c32>] __sock_sendmsg+0x52/0xa0 net/socket.c:745 [<ffffffff83ec3d7b>] sock_write_iter+0xfb/0x180 net/socket.c:1158 [<ffffffff816961a7>] call_write_iter include/linux/fs.h:2020 [inline] [<ffffffff816961a7>] new_sync_write fs/read_write.c:491 [inline] [<ffffffff816961a7>] vfs_write+0x327/0x590 fs/read_write.c:584 [<ffffffff816966fb>] ksys_write+0x13b/0x170 fs/read_write.c:637 [<ffffffff84b6ddcf>] do_syscall_x64 arch/x86/entry/common.c:51 [inline] [<ffffffff84b6ddcf>] do_syscall_64+0x3f/0x110 arch/x86/entry/common.c:82 [<ffffffff84c0008b>] entry_SYSCALL_64_after_hwframe+0x63/0x6b [Analysis] When creating child nodes, if not all child nodes used to store indexes are created, so the child nodes created before the failure should be released. Reported-and-tested-by: syzbot+006987d1be3586e13555@xxxxxxxxxxxxxxxxxxxxxxxxx Signed-off-by: Lizhi Xu <lizhi.xu@xxxxxxxxxxxxx> --- lib/radix-tree.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/lib/radix-tree.c b/lib/radix-tree.c index a89df8afa510..c5caf5b7523a 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c @@ -616,9 +616,10 @@ static int __radix_tree_create(struct radix_tree_root *root, struct radix_tree_node *node = NULL, *child; 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,6 +629,7 @@ 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); } @@ -637,8 +639,10 @@ 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; + if (!child) { + ret = -ENOMEM; + goto freec; + } rcu_assign_pointer(*slot, node_to_entry(child)); if (node) node->count++; @@ -656,6 +660,17 @@ static int __radix_tree_create(struct radix_tree_root *root, if (slotp) *slotp = slot; return 0; +freec: + if (mmshift > 0) { + struct radix_tree_node *pn; + while (shift < mmshift && node) { + pn = node->parent; + radix_tree_node_rcu_free(&node->rcu_head); + shift += RADIX_TREE_MAP_SHIFT; + node = pn; + } + } + return ret; } /* -- 2.43.0