Threads created before the first user-events registration share an
mm_struct but can each retain a NULL user_event_mm pointer. After one
thread registers an event and publishes a user_event_mm for the shared
address space, a pre-existing sibling can otherwise allocate a second
wrapper with an independent enabler list.

When duplicate-enabler lookup runs for a task with no user_event_mm,
look up the wrapper already associated with current->mm and attach the
task to it. The group reg_mutex serializes registration paths, while
user_event_mms_lock protects the global lookup and interlocks attachment
with the final task-count decrement and list removal. Increment tasks
before publishing the wrapper through current->user_event_mm so exit or
exec will account for the attachment.

Hold user_event_mms_lock only for lookup, reference accounting, and task
attachment. Release it before acquiring event_mutex to inspect the
shared enabler list.

user_event_mm_attach() cannot be reused for this operation because it
publishes a newly allocated wrapper on the global list rather than
attaching a task to an already-published wrapper.

Signed-off-by: Jeff Barnes <[email protected]>
---
 kernel/trace/trace_events_user.c | 44 +++++++++++++++++++++++++++-----
 1 file changed, 37 insertions(+), 7 deletions(-)

diff --git a/kernel/trace/trace_events_user.c b/kernel/trace/trace_events_user.c
index 24983f68d075..da07b873cd48 100644
--- a/kernel/trace/trace_events_user.c
+++ b/kernel/trace/trace_events_user.c
@@ -754,6 +754,20 @@ static struct user_event_mm *user_event_mm_alloc(struct 
task_struct *t)
        return user_mm;
 }
 
+static struct user_event_mm *
+user_event_mm_find_locked(struct mm_struct *mm)
+{
+       struct user_event_mm *user_mm;
+
+       lockdep_assert_held(&user_event_mms_lock);
+
+       list_for_each_entry(user_mm, &user_event_mms, mms_link)
+               if (user_mm->mm == mm)
+                       return user_mm;
+
+       return NULL;
+}
+
 static void user_event_mm_attach(struct user_event_mm *user_mm, struct 
task_struct *t)
 {
        unsigned long flags;
@@ -817,14 +831,18 @@ void user_event_mm_remove(struct task_struct *t)
        might_sleep();
 
        mm = t->user_event_mm;
+
+       spin_lock_irqsave(&user_event_mms_lock, flags);
+
        t->user_event_mm = NULL;
 
-       /* Clone will increment the tasks, only remove if last clone */
-       if (!refcount_dec_and_test(&mm->tasks))
+       /* Clones and attached tasks increment this count. */
+       if (!refcount_dec_and_test(&mm->tasks)) {
+               spin_unlock_irqrestore(&user_event_mms_lock, flags);
                return;
+       }
 
-       /* Remove the mm from the list, so it can no longer be enabled */
-       spin_lock_irqsave(&user_event_mms_lock, flags);
+       /* Prevent new tasks from attaching after the last-task transition. */
        list_del_rcu(&mm->mms_link);
        spin_unlock_irqrestore(&user_event_mms_lock, flags);
 
@@ -894,11 +912,25 @@ static bool
 current_user_event_enabler_exists(struct user_event_group *group,
                                  unsigned long uaddr, unsigned char bit)
 {
-       struct user_event_mm *user_mm = current_user_event_mm();
+       struct user_event_mm *user_mm = current->user_event_mm;
+       unsigned long flags;
        bool exists;
 
        lockdep_assert_held(&group->reg_mutex);
 
+       if (!user_mm) {
+               spin_lock_irqsave(&user_event_mms_lock, flags);
+
+               user_mm = user_event_mm_find_locked(current->mm);
+
+               if (user_mm) {
+                       refcount_inc(&user_mm->tasks);
+                       current->user_event_mm = user_mm;
+               }
+
+               spin_unlock_irqrestore(&user_event_mms_lock, flags);
+       }
+
        if (!user_mm)
                return false;
 
@@ -906,8 +938,6 @@ current_user_event_enabler_exists(struct user_event_group 
*group,
        exists = user_event_enabler_exists(user_mm, uaddr, bit);
        mutex_unlock(&event_mutex);
 
-       user_event_mm_put(user_mm);
-
        return exists;
 }
 
-- 
2.43.0


Reply via email to