Skip to content

Commit 52fce64

Browse files
aeglbp3tk0v
authored andcommitted
fs/resctrl: Fix use-after-free during unmount
During unmount or failure teardown all mon_data structures that contain monitoring event file private data are freed after which kernfs nodes are removed. However, the RDT_DELETED flag is never set for the statically allocated default resource group. A concurrent reader of an event file associated with the default resource group may, after dropping kernfs active protection, block on rdtgroup_mutex while unmount proceeds to free the file private data and destroy the kernfs node without waiting for the reader. When the mutex is released, the reader wakes up, observes that RDT_DELETED is not set for the default group, and dereferences the already-freed file private data. The scenario can be depicted as follows: CPU0 CPU1 /* * Default resource group's * monitoring data accessible via * kernfs file with kernfs_node::priv * pointing to a struct mon_data. * User opens the file for reading. */ rdtgroup_mondata_show() /* arch encounters fatal error */ rdtgroup_kn_lock_live() resctrl_exit() atomic_inc(&rdtgroup_default.waitcount) cpus_read_lock() kernfs_break_active_protection(kn) mutex_lock(&rdtgroup_mutex) cpus_read_lock() resctrl_fs_teardown() mutex_lock(&rdtgroup_mutex) rmdir_all_sub() mon_put_kn_priv() /* Delete all mon_data structures */ rdtgroup_destroy_root() kernfs_destroy_root() rdtgroup_default.kn = NULL mutex_unlock(&rdtgroup_mutex) /* * rdtgroup_default.flags is empty so * rdtgroup_kn_lock_live() returns * &rdtgroup_default */ md = of->kn->priv; /* md points to freed mon_data */ Set RDT_DELETED for the default group unconditionally since the flag does not lead to the freeing of this statically allocated group. Do not allow a new resctrl mount if there are any waiters on default group of previous mount. A new mount will re-initialize the default group that would appear to waiters from previous mount as though the default group is accessible causing them to access the mon_data structures from the previous mount that have been removed. Fixes: 2a65660 ("x86/resctrl: Expand the width of domid by replacing mon_data_bits") Closes: https://sashiko.dev/#/patchset/20260508182143.14592-1-tony.luck%40intel.com?part=2 [1] Reported-by: Sashiko <sashiko-bot@kernel.org> Signed-off-by: Tony Luck <tony.luck@intel.com> Signed-off-by: Reinette Chatre <reinette.chatre@intel.com> Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de> Reviewed-by: Chen Yu <yu.c.chen@intel.com> Cc: <stable@kernel.org> Link: https://patch.msgid.link/49a2ca3ca688f27e1a646cf90e1dc69287021127.1783377598.git.reinette.chatre@intel.com
1 parent ca0676a commit 52fce64

1 file changed

Lines changed: 16 additions & 2 deletions

File tree

fs/resctrl/rdtgroup.c

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -587,14 +587,20 @@ static ssize_t rdtgroup_cpus_write(struct kernfs_open_file *of,
587587
*
588588
* On resource group creation via a mkdir, an extra kernfs_node reference is
589589
* taken to ensure that the rdtgroup structure remains accessible for the
590-
* rdtgroup_kn_unlock() calls where it is removed.
590+
* rdtgroup_kn_unlock() calls where it is removed. The default group is
591+
* statically allocated: it does not have an extra reference but will have
592+
* RDT_DELETED set on unmount to support safe access to its associated files
593+
* via rdtgroup_kn_lock_live/rdtgroup_kn_unlock().
591594
*
592-
* Drop the extra reference here, then free the rdtgroup structure.
595+
* For all but the default group: drop the extra reference, then free the
596+
* rdtgroup structure.
593597
*
594598
* Return: void
595599
*/
596600
static void rdtgroup_remove(struct rdtgroup *rdtgrp)
597601
{
602+
if (rdtgrp == &rdtgroup_default)
603+
return;
598604
kernfs_put(rdtgrp->kn);
599605
kfree(rdtgrp);
600606
}
@@ -2814,6 +2820,12 @@ static int rdt_get_tree(struct fs_context *fc)
28142820
goto out;
28152821
}
28162822

2823+
/* Avoid races from pending operations from a previous mount */
2824+
if (atomic_read(&rdtgroup_default.waitcount) != 0) {
2825+
ret = -EBUSY;
2826+
goto out;
2827+
}
2828+
28172829
ret = setup_rmid_lru_list();
28182830
if (ret)
28192831
goto out;
@@ -3177,6 +3189,7 @@ static void resctrl_fs_teardown(void)
31773189
mon_put_kn_priv();
31783190
rdt_pseudo_lock_release();
31793191
rdtgroup_default.mode = RDT_MODE_SHAREABLE;
3192+
rdtgroup_default.flags = RDT_DELETED;
31803193
closid_exit();
31813194
schemata_list_destroy();
31823195
rdtgroup_destroy_root();
@@ -4277,6 +4290,7 @@ static int rdtgroup_setup_root(struct rdt_fs_context *ctx)
42774290

42784291
ctx->kfc.root = rdt_root;
42794292
rdtgroup_default.kn = kernfs_root_to_node(rdt_root);
4293+
rdtgroup_default.flags = 0;
42804294

42814295
return 0;
42824296
}

0 commit comments

Comments
 (0)