How to detect inode leaks?

From: Steven Rostedt

Date: Fri Oct 30 2026 - 16:12:34 EST


Hi,

While working on a eventfs file system to allow the event format and id
files to be exposed as read-only for unprivileged users, I thought I had an
error path in the fill_super() logic that didn't free the created inode.

Basically this:

inode = new_inode(sb);
if (!inode)
return -ENOMEM;

inode->i_ino = 1;
inode->i_mode = S_IFDIR | 0555;
simple_inode_init_ts(inode);
inode->i_op = &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
set_nlink(inode, 2);

set_default_d_op(sb, &tracefs_dentry_operations);

root = d_make_root(inode);
if (!root)
return -ENOMEM;

Although I later found that d_make_inode() does an iput() on the inode on
failure, I wanted to know if the inode leak would be detected by kmemleak.

Thus I change the above to:

root = NULL; // d_make_root(inode);
if (!root)
return -ENOMEM;

To force the leak. I booted this and ran:

~# while :; do mount -t eventfs nodev /sys/kernel/events/; done
mount: /sys/kernel/events: fsconfig() failed: Cannot allocate memory.
dmesg(1) may have more information after failed mount system call.
mount: /sys/kernel/events: fsconfig() failed: Cannot allocate memory.
dmesg(1) may have more information after failed mount system call.
mount: /sys/kernel/events: fsconfig() failed: Cannot allocate memory.
dmesg(1) may have more information after failed mount system call.
mount: /sys/kernel/events: fsconfig() failed: Cannot allocate memory.
dmesg(1) may have more information after failed mount system call.
mount: /sys/kernel/events: fsconfig() failed: Cannot allocate memory.
dmesg(1) may have more information after failed mount system call.
mount: /sys/kernel/events: fsconfig() failed: Cannot allocate memory.
dmesg(1) may have more information after failed mount system call.
mount: /sys/kernel/events: fsconfig() failed: Cannot allocate memory.
dmesg(1) may have more information after failed mount system call.
mount: /sys/kernel/events: fsconfig() failed: Cannot allocate memory.
dmesg(1) may have more information after failed mount system call.
mount: /sys/kernel/events: fsconfig() failed: Cannot allocate memory.
dmesg(1) may have more information after failed mount system call.
mount: /sys/kernel/events: fsconfig() failed: Cannot allocate memory.
dmesg(1) may have more information after failed mount system call.
[..]

Let it go for some time and waited for kmemleak to find it.

It never did.

I also added a:

~# echo 1 > /proc/sys/vm/drop_caches

And that still didn't trigger anything.

I did some tracing and this time I only ran the mount once.

In one window I did:

~# echo 1 > /proc/sys/vm/drop_caches
~# trace-cmd record -p function --func-stack -l alloc_inode -B graph -p function_graph -l alloc_inode -l kmem_cache_free -O funcgraph-args -O funcgraph-retval

And another window I ran:

~# mount -t eventfs nodev /sys/kernel/events/
~# echo 1 > /proc/sys/vm/drop_caches

Waited and stopped the recording. The report had:

graph: mount-1213 [002] ...2. 2088.465177: funcgraph_entry: ! 456.541 us | alloc_inode(sb=0xffff88812bf8e000); (ret=0xffff88813d489860)
mount-1213 [002] ...1. 2088.465180: function: alloc_inode
mount-1213 [002] ...1. 2088.465197: kernel_stack: <stack trace >
=> arch_ftrace_ops_list_func (ffffffff81bdf377)
=> ftrace_call (ffffffff816e5d85)
=> alloc_inode (ffffffff82491559)
=> new_inode (ffffffff82491757)
=> eventfs_ro_fill_super (ffffffff82990f2b)
=> get_tree_single (ffffffff824184fc)
=> vfs_get_tree (ffffffff8240dc6b)
=> vfs_cmd_create (ffffffff8253ac42)
=> __do_sys_fsconfig (ffffffff8253b28e)
=> do_syscall_64 (ffffffff8499faeb)
=> entry_SYSCALL_64_after_hwframe (ffffffff8100012f)

The ret=0xffff88813d489860 is the return value of the alloc_inode() call in
eventfs_ro_fill_super(). I then searched all the kmem_cache_free() for that
value and couldn't find it.

I then changed the code to:

root = NULL; // d_make_root(inode);
if (!root) {
iput(inode);
return -ENOMEM;
}

Booted and ran the above again. This time I had in the trace:

graph: mount-1134 [005] ...2. 799.112115: funcgraph_entry: ! 816.295 us | alloc_inode(sb=0xffff888140556000); (ret=0xffff8881514f4920)
mount-1134 [005] ...1. 799.112117: function: alloc_inode
mount-1134 [005] ...1. 799.112133: kernel_stack: <stack trace >
=> arch_ftrace_ops_list_func (ffffffff81bdf377)
=> ftrace_call (ffffffff816e5d85)
=> alloc_inode (ffffffff82491559)
=> new_inode (ffffffff82491757)
=> eventfs_ro_fill_super (ffffffff82990f28)
=> get_tree_single (ffffffff824184fc)
=> vfs_get_tree (ffffffff8240dc6b)
=> vfs_cmd_create (ffffffff8253ac42)
=> __do_sys_fsconfig (ffffffff8253b28e)
=> do_syscall_64 (ffffffff8499faeb)
=> entry_SYSCALL_64_after_hwframe (ffffffff8100012f)

But I also found:

graph: ksoftirqd/5-43 [005] ..s1. 799.131415: funcgraph_entry: + 23.788 us | kmem_cache_free(s=0xffff888100dc0500, x=0xffff8881514f4920); (ret=0x0)


Which shows that the inode was freed when I do a iput(inode) (which would
never happen if the function exited early).

This got me thinking. If for some reason any use case of alloc_inode() did
not free it, kmemleak will not catch it. I'm guessing because the inode is
referenced by something somewhere (where it requires the drop_caches or
memory pressure to free it).

My question is, is there any mechanism that can catch leaked inodes?

-- Steve