Skip to content

Commit 9568880

Browse files
Al Virogregkh
authored andcommitted
debugfs: don't mess with bits in ->d_fsdata
The reason we need that crap is the dual use ->d_fsdata has there - it's both holding a debugfs_fsdata reference after the first debugfs_file_get() (actually, after the call of proxy ->open()) *and* it serves as a place to stash a reference to real file_operations from object creation to the first open. Oh, and it's triple use, actually - that stashed reference might be to debugfs_short_fops. Bugger that for a game of solidiers - just put the operations reference into debugfs-private augmentation of inode. And split debugfs_full_file_operations into full and short cases, so that debugfs_get_file() could tell one from another. Voila - ->d_fsdata holds NULL until the first (successful) debugfs_get_file() and a reference to struct debugfs_fsdata afterwards. Signed-off-by: Al Viro <[email protected]> Reviewed-by: Christian Brauner <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 41a0ecc commit 9568880

File tree

3 files changed

+38
-61
lines changed

3 files changed

+38
-61
lines changed

fs/debugfs/file.c

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ const struct file_operations *debugfs_real_fops(const struct file *filp)
5151
{
5252
struct debugfs_fsdata *fsd = F_DENTRY(filp)->d_fsdata;
5353

54-
if ((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT) {
54+
if (!fsd) {
5555
/*
5656
* Urgh, we've been called w/o a protecting
5757
* debugfs_file_get().
@@ -84,9 +84,11 @@ static int __debugfs_file_get(struct dentry *dentry, enum dbgfs_get_mode mode)
8484
return -EINVAL;
8585

8686
d_fsd = READ_ONCE(dentry->d_fsdata);
87-
if (!((unsigned long)d_fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)) {
87+
if (d_fsd) {
8888
fsd = d_fsd;
8989
} else {
90+
struct inode *inode = dentry->d_inode;
91+
9092
if (WARN_ON(mode == DBGFS_GET_ALREADY))
9193
return -EINVAL;
9294

@@ -96,9 +98,7 @@ static int __debugfs_file_get(struct dentry *dentry, enum dbgfs_get_mode mode)
9698

9799
if (mode == DBGFS_GET_SHORT) {
98100
const struct debugfs_short_fops *ops;
99-
ops = (void *)((unsigned long)d_fsd &
100-
~DEBUGFS_FSDATA_IS_REAL_FOPS_BIT);
101-
fsd->short_fops = ops;
101+
ops = fsd->short_fops = DEBUGFS_I(inode)->short_fops;
102102
if (ops->llseek)
103103
fsd->methods |= HAS_LSEEK;
104104
if (ops->read)
@@ -107,9 +107,7 @@ static int __debugfs_file_get(struct dentry *dentry, enum dbgfs_get_mode mode)
107107
fsd->methods |= HAS_WRITE;
108108
} else {
109109
const struct file_operations *ops;
110-
ops = (void *)((unsigned long)d_fsd &
111-
~DEBUGFS_FSDATA_IS_REAL_FOPS_BIT);
112-
fsd->real_fops = ops;
110+
ops = fsd->real_fops = DEBUGFS_I(inode)->real_fops;
113111
if (ops->llseek)
114112
fsd->methods |= HAS_LSEEK;
115113
if (ops->read)
@@ -126,10 +124,11 @@ static int __debugfs_file_get(struct dentry *dentry, enum dbgfs_get_mode mode)
126124
INIT_LIST_HEAD(&fsd->cancellations);
127125
mutex_init(&fsd->cancellations_mtx);
128126

129-
if (cmpxchg(&dentry->d_fsdata, d_fsd, fsd) != d_fsd) {
127+
d_fsd = cmpxchg(&dentry->d_fsdata, NULL, fsd);
128+
if (d_fsd) {
130129
mutex_destroy(&fsd->cancellations_mtx);
131130
kfree(fsd);
132-
fsd = READ_ONCE(dentry->d_fsdata);
131+
fsd = d_fsd;
133132
}
134133
}
135134

@@ -226,8 +225,7 @@ void debugfs_enter_cancellation(struct file *file,
226225
return;
227226

228227
fsd = READ_ONCE(dentry->d_fsdata);
229-
if (WARN_ON(!fsd ||
230-
((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)))
228+
if (WARN_ON(!fsd))
231229
return;
232230

233231
mutex_lock(&fsd->cancellations_mtx);
@@ -258,8 +256,7 @@ void debugfs_leave_cancellation(struct file *file,
258256
return;
259257

260258
fsd = READ_ONCE(dentry->d_fsdata);
261-
if (WARN_ON(!fsd ||
262-
((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)))
259+
if (WARN_ON(!fsd))
263260
return;
264261

265262
mutex_lock(&fsd->cancellations_mtx);
@@ -427,22 +424,21 @@ static int full_proxy_release(struct inode *inode, struct file *filp)
427424
* not to leak any resources. Releasers must not assume that
428425
* ->i_private is still being meaningful here.
429426
*/
430-
if (real_fops && real_fops->release)
427+
if (real_fops->release)
431428
r = real_fops->release(inode, filp);
432429

433430
fops_put(real_fops);
434431
return r;
435432
}
436433

437-
static int full_proxy_open(struct inode *inode, struct file *filp,
438-
enum dbgfs_get_mode mode)
434+
static int full_proxy_open_regular(struct inode *inode, struct file *filp)
439435
{
440436
struct dentry *dentry = F_DENTRY(filp);
441437
const struct file_operations *real_fops;
442438
struct debugfs_fsdata *fsd;
443439
int r;
444440

445-
r = __debugfs_file_get(dentry, mode);
441+
r = __debugfs_file_get(dentry, DBGFS_GET_REGULAR);
446442
if (r)
447443
return r == -EIO ? -ENOENT : r;
448444

@@ -452,7 +448,7 @@ static int full_proxy_open(struct inode *inode, struct file *filp,
452448
if (r)
453449
goto out;
454450

455-
if (real_fops && !fops_get(real_fops)) {
451+
if (!fops_get(real_fops)) {
456452
#ifdef CONFIG_MODULES
457453
if (real_fops->owner &&
458454
real_fops->owner->state == MODULE_STATE_GOING) {
@@ -468,11 +464,8 @@ static int full_proxy_open(struct inode *inode, struct file *filp,
468464
goto out;
469465
}
470466

471-
if (!real_fops || real_fops->open) {
472-
if (real_fops)
473-
r = real_fops->open(inode, filp);
474-
else
475-
r = simple_open(inode, filp);
467+
if (real_fops->open) {
468+
r = real_fops->open(inode, filp);
476469
if (r) {
477470
fops_put(real_fops);
478471
} else if (filp->f_op != &debugfs_full_proxy_file_operations) {
@@ -487,11 +480,6 @@ static int full_proxy_open(struct inode *inode, struct file *filp,
487480
return r;
488481
}
489482

490-
static int full_proxy_open_regular(struct inode *inode, struct file *filp)
491-
{
492-
return full_proxy_open(inode, filp, DBGFS_GET_REGULAR);
493-
}
494-
495483
const struct file_operations debugfs_full_proxy_file_operations = {
496484
.open = full_proxy_open_regular,
497485
.release = full_proxy_release,
@@ -504,7 +492,17 @@ const struct file_operations debugfs_full_proxy_file_operations = {
504492

505493
static int full_proxy_open_short(struct inode *inode, struct file *filp)
506494
{
507-
return full_proxy_open(inode, filp, DBGFS_GET_SHORT);
495+
struct dentry *dentry = F_DENTRY(filp);
496+
int r;
497+
498+
r = __debugfs_file_get(dentry, DBGFS_GET_SHORT);
499+
if (r)
500+
return r == -EIO ? -ENOENT : r;
501+
r = debugfs_locked_down(inode, filp, NULL);
502+
if (!r)
503+
r = simple_open(inode, filp);
504+
debugfs_file_put(dentry);
505+
return r;
508506
}
509507

510508
const struct file_operations debugfs_full_short_proxy_file_operations = {

fs/debugfs/inode.c

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -243,15 +243,10 @@ static void debugfs_release_dentry(struct dentry *dentry)
243243
{
244244
struct debugfs_fsdata *fsd = dentry->d_fsdata;
245245

246-
if ((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)
247-
return;
248-
249-
/* check it wasn't a dir or automount (no fsdata) */
250246
if (fsd) {
251247
WARN_ON(!list_empty(&fsd->cancellations));
252248
mutex_destroy(&fsd->cancellations_mtx);
253249
}
254-
255250
kfree(fsd);
256251
}
257252

@@ -459,9 +454,10 @@ static struct dentry *__debugfs_create_file(const char *name, umode_t mode,
459454
inode->i_private = data;
460455

461456
inode->i_op = &debugfs_file_inode_operations;
457+
if (!real_fops)
458+
proxy_fops = &debugfs_noop_file_operations;
462459
inode->i_fop = proxy_fops;
463-
dentry->d_fsdata = (void *)((unsigned long)real_fops |
464-
DEBUGFS_FSDATA_IS_REAL_FOPS_BIT);
460+
DEBUGFS_I(inode)->raw = real_fops;
465461

466462
d_instantiate(dentry, inode);
467463
fsnotify_create(d_inode(dentry->d_parent), dentry);
@@ -472,13 +468,8 @@ struct dentry *debugfs_create_file_full(const char *name, umode_t mode,
472468
struct dentry *parent, void *data,
473469
const struct file_operations *fops)
474470
{
475-
if (WARN_ON((unsigned long)fops &
476-
DEBUGFS_FSDATA_IS_REAL_FOPS_BIT))
477-
return ERR_PTR(-EINVAL);
478-
479471
return __debugfs_create_file(name, mode, parent, data,
480-
fops ? &debugfs_full_proxy_file_operations :
481-
&debugfs_noop_file_operations,
472+
&debugfs_full_proxy_file_operations,
482473
fops);
483474
}
484475
EXPORT_SYMBOL_GPL(debugfs_create_file_full);
@@ -487,13 +478,8 @@ struct dentry *debugfs_create_file_short(const char *name, umode_t mode,
487478
struct dentry *parent, void *data,
488479
const struct debugfs_short_fops *fops)
489480
{
490-
if (WARN_ON((unsigned long)fops &
491-
DEBUGFS_FSDATA_IS_REAL_FOPS_BIT))
492-
return ERR_PTR(-EINVAL);
493-
494481
return __debugfs_create_file(name, mode, parent, data,
495-
fops ? &debugfs_full_short_proxy_file_operations :
496-
&debugfs_noop_file_operations,
482+
&debugfs_full_short_proxy_file_operations,
497483
fops);
498484
}
499485
EXPORT_SYMBOL_GPL(debugfs_create_file_short);
@@ -531,8 +517,7 @@ struct dentry *debugfs_create_file_unsafe(const char *name, umode_t mode,
531517
{
532518

533519
return __debugfs_create_file(name, mode, parent, data,
534-
fops ? &debugfs_open_proxy_file_operations :
535-
&debugfs_noop_file_operations,
520+
&debugfs_open_proxy_file_operations,
536521
fops);
537522
}
538523
EXPORT_SYMBOL_GPL(debugfs_create_file_unsafe);
@@ -737,7 +722,7 @@ static void __debugfs_file_removed(struct dentry *dentry)
737722
*/
738723
smp_mb();
739724
fsd = READ_ONCE(dentry->d_fsdata);
740-
if ((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)
725+
if (!fsd)
741726
return;
742727

743728
/* if this was the last reference, we're done */

fs/debugfs/internal.h

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ struct file_operations;
1414
struct debugfs_inode_info {
1515
struct inode vfs_inode;
1616
union {
17+
const void *raw;
18+
const struct file_operations *real_fops;
19+
const struct debugfs_short_fops *short_fops;
1720
debugfs_automount_t automount;
1821
};
1922
};
@@ -51,15 +54,6 @@ enum {
5154
HAS_IOCTL = 16
5255
};
5356

54-
/*
55-
* A dentry's ->d_fsdata either points to the real fops or to a
56-
* dynamically allocated debugfs_fsdata instance.
57-
* In order to distinguish between these two cases, a real fops
58-
* pointer gets its lowest bit set.
59-
*/
60-
#define DEBUGFS_FSDATA_IS_REAL_FOPS_BIT BIT(0)
61-
62-
/* Access BITS */
6357
#define DEBUGFS_ALLOW_API BIT(0)
6458
#define DEBUGFS_ALLOW_MOUNT BIT(1)
6559

0 commit comments

Comments
 (0)