Skip to content

Commit 77a06c3

Browse files
committed
eventfs: Test for ei->is_freed when accessing ei->dentry
The eventfs_inode (ei) is protected by SRCU, but the ei->dentry is not. It is protected by the eventfs_mutex. Anytime the eventfs_mutex is released, and access to the ei->dentry needs to be done, it should first check if ei->is_freed is set under the eventfs_mutex. If it is, then the ei->dentry is invalid and must not be used. The ei->dentry must only be accessed under the eventfs_mutex and after checking if ei->is_freed is set. When the ei is being freed, it will (under the eventfs_mutex) set is_freed and at the same time move the dentry to a free list to be cleared after the eventfs_mutex is released. This means that any access to the ei->dentry must check first if ei->is_freed is set, because if it is, then the dentry is on its way to be freed. Also add comments to describe this better. Link: https://lore.kernel.org/all/CA+G9fYt6pY+tMZEOg=SoEywQOe19fGP3uR15SGowkdK+_X85Cg@mail.gmail.com/ Link: https://lore.kernel.org/all/CA+G9fYuDP3hVQ3t7FfrBAjd_WFVSurMgCepTxunSJf=MTe=6aA@mail.gmail.com/ Link: https://lkml.kernel.org/r/20231101172649.477608228@goodmis.org Cc: Ajay Kaher <akaher@vmware.com> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Andrew Morton <akpm@linux-foundation.org> Fixes: 5790b1f ("eventfs: Remove eventfs_file and just use eventfs_inode") Reported-by: Linux Kernel Functional Testing <lkft@linaro.org> Reported-by: Naresh Kamboju <naresh.kamboju@linaro.org> Reported-by: Beau Belgrave <beaub@linux.microsoft.com> Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> Tested-by: Linux Kernel Functional Testing <lkft@linaro.org> Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org> Tested-by: Beau Belgrave <beaub@linux.microsoft.com> Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
1 parent db3a397 commit 77a06c3

File tree

2 files changed

+41
-7
lines changed

2 files changed

+41
-7
lines changed

fs/tracefs/event_inode.c

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,20 @@
2424
#include <linux/delay.h>
2525
#include "internal.h"
2626

27+
/*
28+
* eventfs_mutex protects the eventfs_inode (ei) dentry. Any access
29+
* to the ei->dentry must be done under this mutex and after checking
30+
* if ei->is_freed is not set. The ei->dentry is released under the
31+
* mutex at the same time ei->is_freed is set. If ei->is_freed is set
32+
* then the ei->dentry is invalid.
33+
*/
2734
static DEFINE_MUTEX(eventfs_mutex);
35+
36+
/*
37+
* The eventfs_inode (ei) itself is protected by SRCU. It is released from
38+
* its parent's list and will have is_freed set (under eventfs_mutex).
39+
* After the SRCU grace period is over, the ei may be freed.
40+
*/
2841
DEFINE_STATIC_SRCU(eventfs_srcu);
2942

3043
static struct dentry *eventfs_root_lookup(struct inode *dir,
@@ -239,6 +252,10 @@ create_file_dentry(struct eventfs_inode *ei, struct dentry **e_dentry,
239252
bool invalidate = false;
240253

241254
mutex_lock(&eventfs_mutex);
255+
if (ei->is_freed) {
256+
mutex_unlock(&eventfs_mutex);
257+
return NULL;
258+
}
242259
/* If the e_dentry already has a dentry, use it */
243260
if (*e_dentry) {
244261
/* lookup does not need to up the ref count */
@@ -312,6 +329,8 @@ static void eventfs_post_create_dir(struct eventfs_inode *ei)
312329
struct eventfs_inode *ei_child;
313330
struct tracefs_inode *ti;
314331

332+
lockdep_assert_held(&eventfs_mutex);
333+
315334
/* srcu lock already held */
316335
/* fill parent-child relation */
317336
list_for_each_entry_srcu(ei_child, &ei->children, list,
@@ -325,19 +344,25 @@ static void eventfs_post_create_dir(struct eventfs_inode *ei)
325344

326345
/**
327346
* create_dir_dentry - Create a directory dentry for the eventfs_inode
347+
* @pei: The eventfs_inode parent of ei.
328348
* @ei: The eventfs_inode to create the directory for
329349
* @parent: The dentry of the parent of this directory
330350
* @lookup: True if this is called by the lookup code
331351
*
332352
* This creates and attaches a directory dentry to the eventfs_inode @ei.
333353
*/
334354
static struct dentry *
335-
create_dir_dentry(struct eventfs_inode *ei, struct dentry *parent, bool lookup)
355+
create_dir_dentry(struct eventfs_inode *pei, struct eventfs_inode *ei,
356+
struct dentry *parent, bool lookup)
336357
{
337358
bool invalidate = false;
338359
struct dentry *dentry = NULL;
339360

340361
mutex_lock(&eventfs_mutex);
362+
if (pei->is_freed || ei->is_freed) {
363+
mutex_unlock(&eventfs_mutex);
364+
return NULL;
365+
}
341366
if (ei->dentry) {
342367
/* If the dentry already has a dentry, use it */
343368
dentry = ei->dentry;
@@ -440,7 +465,7 @@ static struct dentry *eventfs_root_lookup(struct inode *dir,
440465
*/
441466
mutex_lock(&eventfs_mutex);
442467
ei = READ_ONCE(ti->private);
443-
if (ei)
468+
if (ei && !ei->is_freed)
444469
ei_dentry = READ_ONCE(ei->dentry);
445470
mutex_unlock(&eventfs_mutex);
446471

@@ -454,7 +479,7 @@ static struct dentry *eventfs_root_lookup(struct inode *dir,
454479
if (strcmp(ei_child->name, name) != 0)
455480
continue;
456481
ret = simple_lookup(dir, dentry, flags);
457-
create_dir_dentry(ei_child, ei_dentry, true);
482+
create_dir_dentry(ei, ei_child, ei_dentry, true);
458483
created = true;
459484
break;
460485
}
@@ -588,7 +613,7 @@ static int dcache_dir_open_wrapper(struct inode *inode, struct file *file)
588613

589614
list_for_each_entry_srcu(ei_child, &ei->children, list,
590615
srcu_read_lock_held(&eventfs_srcu)) {
591-
d = create_dir_dentry(ei_child, parent, false);
616+
d = create_dir_dentry(ei, ei_child, parent, false);
592617
if (d) {
593618
ret = add_dentries(&dentries, d, cnt);
594619
if (ret < 0)
@@ -705,12 +730,20 @@ struct eventfs_inode *eventfs_create_dir(const char *name, struct eventfs_inode
705730
ei->nr_entries = size;
706731
ei->data = data;
707732
INIT_LIST_HEAD(&ei->children);
733+
INIT_LIST_HEAD(&ei->list);
708734

709735
mutex_lock(&eventfs_mutex);
710-
list_add_tail(&ei->list, &parent->children);
711-
ei->d_parent = parent->dentry;
736+
if (!parent->is_freed) {
737+
list_add_tail(&ei->list, &parent->children);
738+
ei->d_parent = parent->dentry;
739+
}
712740
mutex_unlock(&eventfs_mutex);
713741

742+
/* Was the parent freed? */
743+
if (list_empty(&ei->list)) {
744+
free_ei(ei);
745+
ei = NULL;
746+
}
714747
return ei;
715748
}
716749

fs/tracefs/internal.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,15 @@ struct tracefs_inode {
2424
* @d_children: The array of dentries to represent the files when created
2525
* @data: The private data to pass to the callbacks
2626
* @is_freed: Flag set if the eventfs is on its way to be freed
27+
* Note if is_freed is set, then dentry is corrupted.
2728
* @nr_entries: The number of items in @entries
2829
*/
2930
struct eventfs_inode {
3031
struct list_head list;
3132
const struct eventfs_entry *entries;
3233
const char *name;
3334
struct list_head children;
34-
struct dentry *dentry;
35+
struct dentry *dentry; /* Check is_freed to access */
3536
struct dentry *d_parent;
3637
struct dentry **d_children;
3738
void *data;

0 commit comments

Comments
 (0)