+ fault-inject-fix-dentry-leak.patch added to mm-nonmm-unstable branch
Andrew Morton <[email protected]>
| Newsgroups | org.kernel.vger.mm-commits,org.kernel.vger.stable |
|---|---|
| Message-ID | <[email protected]> |
The patch titled
Subject: fault-inject: fix dentry leak
has been added to the -mm mm-nonmm-unstable branch. Its filename is
fault-inject-fix-dentry-leak.patch
This patch will shortly appear at
https://git.kernel.org/pub/scm/linux/kernel/git/akpm/25-new.git/tree/patches/fault-inject-fix-dentry-leak.patch
This patch will later appear in the mm-nonmm-unstable branch at
git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
Before you just go and hit "reply", please:
a) Consider who else should be cc'ed
b) Prefer to cc a suitable mailing list as well
c) Ideally: find the original patch on the mailing list and do a
reply-to-all to that, adding suitable additional cc's
*** Remember to use Documentation/process/submit-checklist.rst when testing your code ***
The -mm tree is included into linux-next via various
branches at git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
and is updated there most days
------------------------------------------------------
From: Michael Liang <[email protected]>
Subject: fault-inject: fix dentry leak
Date: Fri, 21 Aug 2026 12:15:27 -0600
fault_create_debugfs_attr() has always taken an extra dentry reference on
the created directory (attr->dname = dget(dir)) so that fail_dump() could
print the name via %pd from any context. Nothing anywhere in the tree
ever calls dput() on attr->dname.
For callers with a matching teardown, that unmatched reference causes one
dentry plus its attached inode to leak per fault_create_debugfs_attr /
debugfs_remove_recursive cycle. simple_recursive_removal() drops
debugfs's own +1 ref on the child dentry, but the dget()'d ref keeps its
refcount at 1: the dentry ends up unhashed but pinned, and its inode is
never freed.
Boot-once callers (mm/failslab, block/blk-core, etc.) leak exactly once at
init and never destroy the tree, so the impact there is bounded. But
per-lifecycle callers (drivers/nvme, drivers/infiniband/hw/hfi1,
drivers/mmc, drivers/iommu/iommufd, drivers/media, drivers/misc,
drivers/gpu/drm/msm, drivers/crypto, net/sunrpc) leak on every
create/destroy cycle.
We observed this in production: an NVMe/RDMA host repeatedly reconnecting
to a target that rejected the CRTO Property Get went through ~50 nvme
controller create/destroy cycles per second, and dentry and inode_cache
grew by ~13k pinned objects per 240 s -- unrecoverable through
drop_caches. Byte math matched a per-cycle 1-dentry / 1-inode leak from
the "fault_inject" directory dentry.
Fix this by not holding any external reference in fault_attr. Embed the
directory name as a fixed-size char array (FAULT_ATTR_DNAME_LEN, 64 bytes)
inside struct fault_attr, copied by strscpy() at
fault_create_debugfs_attr() time. fail_dump() prints it via %s.
Advantages of an embedded array over kstrdup() + kfree() paired with a new
destroy API:
- Zero API footprint. No new export and no caller changes required:
callers already own their fault_attr's memory and free it when
they are done, and now that suffices.
- No allocation on the create path.
- fault_create_debugfs_attr() cannot fail from the name-copy step.
- No lifetime coupling between attr->dname and debugfs; the string
is valid for exactly as long as the containing struct.
The 64-byte length accommodates every in-tree caller with generous
headroom (the longest current name is "fail_dma_array_full", 19 chars).
The user-visible fail_dump() format changes from "name %pd" to "name %s",
but the printed content is identical -- %pd on the created directory
renders the same string that was passed in as @name.
drivers/infiniband/hw/hfi1/fault.c drops a now-invalid "attr.dname = NULL"
statement; the surrounding kzalloc() already zero-initialises the array.
Link: https://lore.kernel.org/[email protected]
Fixes: 6adc4a22f20b ("fault-inject: add ratelimit option")
Signed-off-by: Michael Liang <[email protected]>
Reviewed-by: Andrew Morton <[email protected]>
Cc: Akinbou Mita <[email protected]>
Cc: Dennis Dalessandro <[email protected]>
Cc: Jason Gunthorpe <[email protected]>
Cc: Leon Romanovsky <[email protected]>
Cc: Vlastimil Babka <[email protected]>
Cc: <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
---
drivers/infiniband/hw/hfi1/fault.c | 1 -
include/linux/fault-inject.h | 10 ++++++++--
lib/fault-inject.c | 7 +++++--
3 files changed, 13 insertions(+), 5 deletions(-)
--- a/drivers/infiniband/hw/hfi1/fault.c~fault-inject-fix-dentry-leak
+++ a/drivers/infiniband/hw/hfi1/fault.c
@@ -216,7 +216,6 @@ int hfi1_fault_init_debugfs(struct hfi1_
ibd->fault->attr.interval = 1;
ibd->fault->attr.require_end = ULONG_MAX;
ibd->fault->attr.stacktrace_depth = 32;
- ibd->fault->attr.dname = NULL;
ibd->fault->attr.verbose = 0;
ibd->fault->enable = false;
ibd->fault->opcode = false;
--- a/include/linux/fault-inject.h~fault-inject-fix-dentry-leak
+++ a/include/linux/fault-inject.h
@@ -19,6 +19,13 @@ enum fault_flags {
#include <linux/ratelimit.h>
/*
+ * Length of the debugfs directory name embedded in struct fault_attr.
+ * Chosen to accommodate every in-tree caller of fault_create_debugfs_attr()
+ * (the longest is "fail_dma_array_full", 19 chars) with generous headroom.
+ */
+#define FAULT_ATTR_DNAME_LEN 64
+
+/*
* For explanation of the elements of this struct, see
* Documentation/fault-injection/fault-injection.rst
*/
@@ -37,7 +44,7 @@ struct fault_attr {
unsigned long count;
struct ratelimit_state ratelimit_state;
- struct dentry *dname;
+ char dname[FAULT_ATTR_DNAME_LEN];
};
#define FAULT_ATTR_INITIALIZER { \
@@ -47,7 +54,6 @@ struct fault_attr {
.stacktrace_depth = 32, \
.ratelimit_state = RATELIMIT_STATE_INIT_DISABLED, \
.verbose = 2, \
- .dname = NULL, \
}
#define DECLARE_FAULT_ATTR(name) struct fault_attr name = FAULT_ATTR_INITIALIZER
--- a/lib/fault-inject.c~fault-inject-fix-dentry-leak
+++ a/lib/fault-inject.c
@@ -5,6 +5,7 @@
#include <linux/debugfs.h>
#include <linux/sched.h>
#include <linux/stat.h>
+#include <linux/string.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/export.h>
@@ -64,7 +65,7 @@ static void fail_dump(struct fault_attr
{
if (attr->verbose > 0 && __ratelimit(&attr->ratelimit_state)) {
printk(KERN_NOTICE "FAULT_INJECTION: forcing a failure.\n"
- "name %pd, interval %lu, probability %lu, "
+ "name %s, interval %lu, probability %lu, "
"space %d, times %d\n", attr->dname,
attr->interval, attr->probability,
atomic_read(&attr->space),
@@ -261,7 +262,9 @@ struct dentry *fault_create_debugfs_attr
debugfs_create_xul("reject-end", mode, dir, &attr->reject_end);
#endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */
- attr->dname = dget(dir);
+ if (strscpy(attr->dname, name, sizeof(attr->dname)) == -E2BIG)
+ pr_warn("FAULT_INJECTION: name '%s' truncated to '%s'\n",
+ name, attr->dname);
return dir;
}
EXPORT_SYMBOL_GPL(fault_create_debugfs_attr);
_
Patches currently in -mm which might be from [email protected] are
fault-inject-fix-dentry-leak.patch