Skip to content

Commit 0ed04a1

Browse files
committed
debugfs: fix automount d_fsdata usage
debugfs_create_automount() stores a function pointer in d_fsdata, but since commit 7c8d469 ("debugfs: add support for more elaborate ->d_fsdata") debugfs_release_dentry() will free it, now conditionally on DEBUGFS_FSDATA_IS_REAL_FOPS_BIT, but that's not set for the function pointer in automount. As a result, removing an automount dentry would attempt to free the function pointer. Luckily, the only user of this (tracing) never removes it. Nevertheless, it's safer if we just handle the fsdata in one way, namely either DEBUGFS_FSDATA_IS_REAL_FOPS_BIT or allocated. Thus, change the automount to allocate it, and use the real_fops in the data to indicate whether or not automount is filled, rather than adding a type tag. At least for now this isn't actually needed, but the next changes will require it. Also check in debugfs_file_get() that it gets only called on regular files, just to make things clearer. Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
1 parent 00f7d15 commit 0ed04a1

3 files changed

Lines changed: 36 additions & 9 deletions

File tree

fs/debugfs/file.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,14 @@ int debugfs_file_get(struct dentry *dentry)
8484
struct debugfs_fsdata *fsd;
8585
void *d_fsd;
8686

87+
/*
88+
* This could only happen if some debugfs user erroneously calls
89+
* debugfs_file_get() on a dentry that isn't even a file, let
90+
* them know about it.
91+
*/
92+
if (WARN_ON(!d_is_reg(dentry)))
93+
return -EINVAL;
94+
8795
d_fsd = READ_ONCE(dentry->d_fsdata);
8896
if (!((unsigned long)d_fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)) {
8997
fsd = d_fsd;

fs/debugfs/inode.c

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -236,17 +236,19 @@ static const struct super_operations debugfs_super_operations = {
236236

237237
static void debugfs_release_dentry(struct dentry *dentry)
238238
{
239-
void *fsd = dentry->d_fsdata;
239+
struct debugfs_fsdata *fsd = dentry->d_fsdata;
240240

241-
if (!((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT))
242-
kfree(dentry->d_fsdata);
241+
if ((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)
242+
return;
243+
244+
kfree(fsd);
243245
}
244246

245247
static struct vfsmount *debugfs_automount(struct path *path)
246248
{
247-
debugfs_automount_t f;
248-
f = (debugfs_automount_t)path->dentry->d_fsdata;
249-
return f(path->dentry, d_inode(path->dentry)->i_private);
249+
struct debugfs_fsdata *fsd = path->dentry->d_fsdata;
250+
251+
return fsd->automount(path->dentry, d_inode(path->dentry)->i_private);
250252
}
251253

252254
static const struct dentry_operations debugfs_dops = {
@@ -634,27 +636,38 @@ struct dentry *debugfs_create_automount(const char *name,
634636
void *data)
635637
{
636638
struct dentry *dentry = start_creating(name, parent);
639+
struct debugfs_fsdata *fsd;
637640
struct inode *inode;
638641

639642
if (IS_ERR(dentry))
640643
return dentry;
641644

645+
fsd = kzalloc(sizeof(*fsd), GFP_KERNEL);
646+
if (!fsd) {
647+
failed_creating(dentry);
648+
return ERR_PTR(-ENOMEM);
649+
}
650+
651+
fsd->automount = f;
652+
642653
if (!(debugfs_allow & DEBUGFS_ALLOW_API)) {
643654
failed_creating(dentry);
655+
kfree(fsd);
644656
return ERR_PTR(-EPERM);
645657
}
646658

647659
inode = debugfs_get_inode(dentry->d_sb);
648660
if (unlikely(!inode)) {
649661
pr_err("out of free dentries, can not create automount '%s'\n",
650662
name);
663+
kfree(fsd);
651664
return failed_creating(dentry);
652665
}
653666

654667
make_empty_dir_inode(inode);
655668
inode->i_flags |= S_AUTOMOUNT;
656669
inode->i_private = data;
657-
dentry->d_fsdata = (void *)f;
670+
dentry->d_fsdata = fsd;
658671
/* directory inodes start off with i_nlink == 2 (for "." entry) */
659672
inc_nlink(inode);
660673
d_instantiate(dentry, inode);

fs/debugfs/internal.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,14 @@ extern const struct file_operations debugfs_full_proxy_file_operations;
1717

1818
struct debugfs_fsdata {
1919
const struct file_operations *real_fops;
20-
refcount_t active_users;
21-
struct completion active_users_drained;
20+
union {
21+
/* automount_fn is used when real_fops is NULL */
22+
debugfs_automount_t automount;
23+
struct {
24+
refcount_t active_users;
25+
struct completion active_users_drained;
26+
};
27+
};
2228
};
2329

2430
/*

0 commit comments

Comments
 (0)