memory leak in radix_tree_insert (3)

Status: upstream: reported syz repro on 2026/11/05 11:10
Subsystems: rdma f2fs
Labels: prio:low
[Documentation on labels]
Reported-by: syzbot+d60be66dd00b4490fa96@syzkaller.appspotmail.com
First crash: 2d01h, last: 2d01h
✨ AI-generated root cause summary:

In lib/radix-tree.c:__radix_tree_create(), intermediate radix_tree_node (xa_node) structures are allocated and linked into parent slots as the function descends through tree levels (shift -= RADIX_TREE_MAP_SHIFT). If a subsequent node allocation fails with -ENOMEM before reaching the leaf slot, __radix_tree_create() returns the error immediately without unwinding or deleting the newly allocated empty intermediate nodes. Consequently, slot-based iterators such as radix_tree_for_each_slot() used during cleanup (e.g. in uverbs_destroy_api() -> uapi_remove_range()) never encounter the empty nodes (which have count == 0), causing those radix_tree_node objects to leak when the containing object is destroyed.

▶ ▼ ✨ Full AI root cause analysis
Based on kernel commit 6628e7d4c6b2:

Root Cause Analysis

1. Functions, Structs, and Files Involved


2. Technical Mechanism and Triggering Path

  1. Intermediate Node Allocation in __radix_tree_create(): When adding an entry to a radix tree (radix_tree_insert() -> __radix_tree_create()), the function walks down the levels of the tree (while (shift > 0)):

    while (shift > 0) {
        shift -= RADIX_TREE_MAP_SHIFT;
        if (child == NULL) {
            child = radix_tree_node_alloc(gfp, node, root, shift, offset, 0, 0);
            if (!child)
                return -ENOMEM;
            rcu_assign_pointer(*slot, node_to_entry(child));
            if (node)
                node->count++;
        } else if (!radix_tree_is_internal_node(child))
            break;
    
        node = entry_to_node(child);
        offset = radix_tree_descend(node, &child, index);
        slot = &node->slots[offset];
    }
    

    At each missing level in the path towards the leaf, an intermediate radix_tree_node is allocated with count = 0 and linked into its parent's slot (*slot), and the parent's count is incremented.

  2. Failure Handling on -ENOMEM: If a tree requires multiple intermediate levels (e.g. shift = 12, then shift = 6, then shift = 0):

    • At shift = 12, intermediate node Object 1 (0xffff88810d78f488, shift = 12, parent = NULL) is allocated.
    • At shift = 6, intermediate node Object 2 (0xffff88810d792920, shift = 6, offset = 9, count = 0, parent = Object 1) is allocated and linked into Object 1->slots[9], incrementing Object 1->count to 1.
    • On the subsequent level (shift = 0), radix_tree_node_alloc() fails with -ENOMEM (in the reproducer, induced via /proc/thread-self/fail-nth = 270).
    • __radix_tree_create() immediately returns -ENOMEM at line 627 without unwinding the tree or calling delete_node() on the partially created path.
  3. Inability to Clean Up Empty Intermediate Nodes:

    • Because radix_tree_insert() returns -ENOMEM, uapi_merge_def() fails, and error recovery invokes uverbs_destroy_api():
      void uverbs_destroy_api(struct uverbs_api *uapi)
      {
          if (!uapi)
              return;
          uapi_remove_range(uapi, 0, U32_MAX);
          kfree(uapi->write_methods);
          kfree(uapi);
      }
      
    • uapi_remove_range() cleans up the tree using radix_tree_for_each_slot():
      static void uapi_remove_range(struct uverbs_api *uapi, u32 start, u32 last)
      {
          struct radix_tree_iter iter;
          void __rcu **slot;
      
          radix_tree_for_each_slot (slot, &uapi->radix, &iter, start) {
              if (iter.index > last)
                  return;
              kfree(rcu_dereference_protected(*slot, true));
              radix_tree_iter_delete(&uapi->radix, &iter, slot);
          }
      }
      
    • radix_tree_for_each_slot() traverses only non-empty leaf slots. Because Object 2 contains only empty slots (count == 0), the iterator never visits Object 2.
    • As valid entries elsewhere in the tree are deleted, delete_node() and radix_tree_shrink() shrink the tree down to Object 1 (shift = 12). However, Object 1->count is still 1 (referencing Object 2), so delete_node() cannot delete Object 1, nor can radix_tree_shrink() collapse it (as offset != 0).
    • Finally, kfree(uapi) frees struct uverbs_api (which embeds uapi->radix at address 0xffff888111f3e540, visible in the xa_node::array pointer field of both nodes' hex dumps). This cuts all remaining references to Object 1 and Object 2, producing the reported kmemleak splat.
▶ ▼ ✨ AI Jobs (1)
ID Workflow Result Correct Bug Created Started Finished Revision
be1009b4 assessment-security DenialOfService: ✅ Exploitable: ❌ FilesystemTrigger: ❌ FuzzingArtifact: ❌ MemoryCorruption: ❌ NetworkNeedsSetup: ❌ NetworkTrigger: ❌ PeripheralTrigger: ❌ RemoteTrigger: ❌ Unprivileged: ❌ UserNamespace: ❌ VMGuestTrigger: ❌ VMHostTrigger: ❌ ❓ memory leak in radix_tree_insert (3) 2026/11/03 13:57 2026/11/03 13:57 2026/11/03 14:36 951ac55c
▶ ▼ Discussions (1)
Title Replies (including bot) Last reply
[syzbot] [rdma?] [f2fs?] memory leak in radix_tree_insert (3) 0 (3) 2026/11/05 13:33
▶ ▼ Similar bugs (2)
Kernel Title Rank 🛈 Repro Cause bisect Fix bisect Count Last Reported Patched Status
upstream memory leak in radix_tree_insert (2) arm-msm net 3 syz, C 1 282d 1063d 29/29 fixed on 2026/05/21 07:49
upstream memory leak in radix_tree_insert arm-msm net 3 syz, C 1 2046d 2046d 20/29 fixed on 2021/11/10 00:50
▶ ▼ Last patch testing requests (2)
Created Duration User Patch Repo Result
2026/11/05 12:21 kartikey406@gmail.com patch upstream running
2026/11/05 13:33 kartikey406@gmail.com patch upstream pending

Sample crash report:
BUG: memory leak
unreferenced object 0xffff88810d78f488 (size 576):
  comm "syz.1.32", pid 7505, jiffies 4294948085
  hex dump (first 32 bytes):
    0c 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    40 e5 f3 11 81 88 ff ff a0 f4 78 0d 81 88 ff ff  @.........x.....
  backtrace (crc d8883653):
    kmemleak_alloc_recursive include/linux/kmemleak.h:44 [inline]
    slab_post_alloc_hook mm/slub.c:4733 [inline]
    slab_alloc_node mm/slub.c:5033 [inline]
    kmem_cache_alloc_noprof+0x35b/0x3e0 mm/slub.c:5047
    radix_tree_node_alloc.constprop.0+0x81/0x1a0 lib/radix-tree.c:276
    __radix_tree_create lib/radix-tree.c:624 [inline]
    radix_tree_insert+0x140/0x390 lib/radix-tree.c:712
    uapi_add_elm drivers/infiniband/core/uverbs_uapi.c:27 [inline]
    uapi_add_elm drivers/infiniband/core/uverbs_uapi.c:16 [inline]
    uapi_add_get_elm+0x61/0x140 drivers/infiniband/core/uverbs_uapi.c:41
    uapi_merge_obj_tree drivers/infiniband/core/uverbs_uapi.c:178 [inline]
    uapi_merge_def+0x4c4/0xb60 drivers/infiniband/core/uverbs_uapi.c:297
    uapi_merge_def+0xf0/0xb60 drivers/infiniband/core/uverbs_uapi.c:286
    uverbs_alloc_api+0x85/0xc50 drivers/infiniband/core/uverbs_uapi.c:659
    ib_uverbs_create_uapi drivers/infiniband/core/uverbs_main.c:1140 [inline]
    ib_uverbs_add_one+0x1b1/0x350 drivers/infiniband/core/uverbs_main.c:1196
    add_client_context+0x290/0x480 drivers/infiniband/core/device.c:755
    enable_device_and_get+0x106/0x250 drivers/infiniband/core/device.c:1375
    ib_register_device drivers/infiniband/core/device.c:1503 [inline]
    ib_register_device+0x4fe/0x7f0 drivers/infiniband/core/device.c:1441
    siw_device_register drivers/infiniband/sw/siw/siw_main.c:71 [inline]
    siw_newlink drivers/infiniband/sw/siw/siw_main.c:433 [inline]
    siw_newlink+0x36a/0x3f0 drivers/infiniband/sw/siw/siw_main.c:415
    nldev_newlink+0x26f/0x3e0 drivers/infiniband/core/nldev.c:1859
    rdma_nl_rcv_msg+0x1ad/0x2d0 drivers/infiniband/core/netlink.c:195
    rdma_nl_rcv_skb.constprop.0.isra.0+0x175/0x1f0 drivers/infiniband/core/netlink.c:239
    netlink_unicast_kernel net/netlink/af_netlink.c:1335 [inline]
    netlink_unicast+0x3e0/0x530 net/netlink/af_netlink.c:1361

BUG: memory leak
unreferenced object 0xffff88810d792920 (size 576):
  comm "syz.1.32", pid 7505, jiffies 4294948085
  hex dump (first 32 bytes):
    06 09 00 00 00 00 00 00 88 f4 78 0d 81 88 ff ff  ..........x.....
    40 e5 f3 11 81 88 ff ff 38 29 79 0d 81 88 ff ff  @.......8)y.....
  backtrace (crc 415aa5b6):
    kmemleak_alloc_recursive include/linux/kmemleak.h:44 [inline]
    slab_post_alloc_hook mm/slub.c:4733 [inline]
    slab_alloc_node mm/slub.c:5033 [inline]
    kmem_cache_alloc_noprof+0x35b/0x3e0 mm/slub.c:5047
    radix_tree_node_alloc.constprop.0+0x81/0x1a0 lib/radix-tree.c:276
    __radix_tree_create lib/radix-tree.c:624 [inline]
    radix_tree_insert+0x140/0x390 lib/radix-tree.c:712
    uapi_add_elm drivers/infiniband/core/uverbs_uapi.c:27 [inline]
    uapi_add_elm drivers/infiniband/core/uverbs_uapi.c:16 [inline]
    uapi_add_get_elm+0x61/0x140 drivers/infiniband/core/uverbs_uapi.c:41
    uapi_merge_obj_tree drivers/infiniband/core/uverbs_uapi.c:178 [inline]
    uapi_merge_def+0x4c4/0xb60 drivers/infiniband/core/uverbs_uapi.c:297
    uapi_merge_def+0xf0/0xb60 drivers/infiniband/core/uverbs_uapi.c:286
    uverbs_alloc_api+0x85/0xc50 drivers/infiniband/core/uverbs_uapi.c:659
    ib_uverbs_create_uapi drivers/infiniband/core/uverbs_main.c:1140 [inline]
    ib_uverbs_add_one+0x1b1/0x350 drivers/infiniband/core/uverbs_main.c:1196
    add_client_context+0x290/0x480 drivers/infiniband/core/device.c:755
    enable_device_and_get+0x106/0x250 drivers/infiniband/core/device.c:1375
    ib_register_device drivers/infiniband/core/device.c:1503 [inline]
    ib_register_device+0x4fe/0x7f0 drivers/infiniband/core/device.c:1441
    siw_device_register drivers/infiniband/sw/siw/siw_main.c:71 [inline]
    siw_newlink drivers/infiniband/sw/siw/siw_main.c:433 [inline]
    siw_newlink+0x36a/0x3f0 drivers/infiniband/sw/siw/siw_main.c:415
    nldev_newlink+0x26f/0x3e0 drivers/infiniband/core/nldev.c:1859
    rdma_nl_rcv_msg+0x1ad/0x2d0 drivers/infiniband/core/netlink.c:195
    rdma_nl_rcv_skb.constprop.0.isra.0+0x175/0x1f0 drivers/infiniband/core/netlink.c:239
    netlink_unicast_kernel net/netlink/af_netlink.c:1335 [inline]
    netlink_unicast+0x3e0/0x530 net/netlink/af_netlink.c:1361

connection error: failed to recv *flatrpc.ExecutorMessageRawT: EOF

Crashes (1):
Time Kernel Commit Syzkaller Config Log Report Syz repro C repro VM info Assets (help?) Manager Title
2026/11/03 13:57 upstream 6628e7d4c6b2 0a94d799 .config console log report syz / log [disk image] [vmlinux] [kernel image] [mounted in repro (corrupt fs)] ci-upstream-gce-leak memory leak in radix_tree_insert
* Struck through repros no longer work on HEAD.