hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I9NZ3E
--------------------------------
Backport the following x86 commit to MPAM to eliminate potential issues:
commit 074fadee59ee ("x86/resctrl: Fix use-after-free due to inaccurate refcount of rdtgroup")
There is a race condition in the following scenario which results in an use-after-free issue when reading a monitoring file and deleting the parent ctrl_mon group concurrently:
Thread 1 calls atomic_inc() to take refcount of rdtgrp and then calls kernfs_break_active_protection() to drop the active reference of kernfs node in rdtgroup_kn_lock_live().
In Thread 2, kernfs_remove() is a blocking routine. It waits on all sub kernfs nodes to drop the active reference when removing all subtree kernfs nodes recursively. Thread 2 could block on kernfs_remove() until Thread 1 calls kernfs_break_active_protection(). Only after kernfs_remove() completes the refcount of rdtgrp could be trusted.
Before Thread 1 calls atomic_inc() and kernfs_break_active_protection(), Thread 2 could call kfree() when the refcount of rdtgrp (sentry) is 0 instead of 1 due to the race.
In Thread 1, in rdtgroup_kn_unlock(), referring to earlier rdtgrp memory (rdtgrp->waitcount) which was already freed in Thread 2 results in use-after-free issue.
Thread 1 (rdtgroup_mondata_show) Thread 2 (rdtgroup_rmdir) -------------------------------- ------------------------- rdtgroup_kn_lock_live /* * kn active protection until * kernfs_break_active_protection(kn) */ rdtgrp = kernfs_to_rdtgroup(kn) rdtgroup_kn_lock_live atomic_inc(&rdtgrp->waitcount) mutex_lock rdtgroup_rmdir_ctrl free_all_child_rdtgrp /* * sentry->waitcount should be 1 * but is 0 now due to the race. */ kfree(sentry)*[1] /* * Only after kernfs_remove() * completes, the refcount of * rdtgrp could be trusted. */ atomic_inc(&rdtgrp->waitcount) /* kn->active-- */ kernfs_break_active_protection(kn) rdtgroup_ctrl_remove rdtgrp->flags = RDT_DELETED /* * Blocking routine, wait for * all sub kernfs nodes to drop * active reference in * kernfs_break_active_protection. */ kernfs_remove(rdtgrp->kn) rdtgroup_kn_unlock mutex_unlock atomic_dec_and_test( &rdtgrp->waitcount) && (flags & RDT_DELETED) kernfs_unbreak_active_protection(kn) kfree(rdtgrp) mutex_lock mon_event_read rdtgroup_kn_unlock mutex_unlock /* * Use-after-free: refer to earlier rdtgrp * memory which was freed in [1]. */ atomic_dec_and_test(&rdtgrp->waitcount) && (flags & RDT_DELETED) /* kn->active++ */ kernfs_unbreak_active_protection(kn) kfree(rdtgrp)
Fix it by moving free_all_child_rdtgrp() to after kernfs_remove() in rdtgroup_rmdir_ctrl() to ensure it has the accurate refcount of rdtgrp.
Fixes: 6065f5513f78 ("resctrlfs: init support resctrlfs") Signed-off-by: Yu Liao liaoyu15@huawei.com --- fs/resctrlfs.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/fs/resctrlfs.c b/fs/resctrlfs.c index 2dc15f551d8e..2c414c71f92f 100644 --- a/fs/resctrlfs.c +++ b/fs/resctrlfs.c @@ -894,19 +894,18 @@ static int resctrl_group_rmdir_ctrl(struct kernfs_node *kn, struct resctrl_group cpumask_or(tmpmask, tmpmask, &rdtgrp->cpu_mask); update_closid_rmid(tmpmask, NULL);
- rdtgrp->flags |= RDT_DELETED; closid_free(rdtgrp->closid.intpartid); rmid_free(rdtgrp->mon.rmid);
+ rdtgrp->flags |= RDT_DELETED; + list_del(&rdtgrp->resctrl_group_list); + + kernfs_remove(rdtgrp->kn); /* * Free all the child monitor group rmids. */ free_all_child_rdtgrp(rdtgrp);
- list_del(&rdtgrp->resctrl_group_list); - - kernfs_remove(rdtgrp->kn); - return 0; }