aboutsummaryrefslogtreecommitdiff
path: root/kernel/events/core.c
diff options
context:
space:
mode:
authorJohn Dias <joaodias@google.com>2016-10-10 14:44:30 -0700
committerMister Oyster <oysterized@gmail.com>2017-04-11 10:59:44 +0200
commit0fa18f53a9d4b875c1033507b08f8677caaad7ef (patch)
treead94d6b1944d4f55f760e9f212e3c257fb86a314 /kernel/events/core.c
parent4b9e26df11bcd4c4c132406f09f17111a0016d4a (diff)
perf: protect group_leader from races that cause ctx double-free
When moving a group_leader perf event from a software-context to a hardware-context, there's a race in checking and updating that context. The existing locking solution doesn't work; note that it tries to grab a lock inside the group_leader's context object, which you can only get at by going through a pointer that should be protected from these races. To avoid that problem, and to produce a simple solution, we can just use a lock per group_leader to protect all checks on the group_leader's context. The new lock is grabbed and released when no context locks are held. Bug: 30955111 Bug: 31095224 Change-Id: If37124c100ca6f4aa962559fba3bd5dbbec8e052
Diffstat (limited to 'kernel/events/core.c')
-rw-r--r--kernel/events/core.c15
1 files changed, 15 insertions, 0 deletions
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 7ab4ad00f..f294f2761 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -6387,6 +6387,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
if (!group_leader)
group_leader = event;
+ mutex_init(&event->group_leader_mutex);
mutex_init(&event->child_mutex);
INIT_LIST_HEAD(&event->child_list);
@@ -6767,6 +6768,16 @@ SYSCALL_DEFINE5(perf_event_open,
group_leader = NULL;
}
+ /*
+ * Take the group_leader's group_leader_mutex before observing
+ * anything in the group leader that leads to changes in ctx,
+ * many of which may be changing on another thread.
+ * In particular, we want to take this lock before deciding
+ * whether we need to move_group.
+ */
+ if (group_leader)
+ mutex_lock(&group_leader->group_leader_mutex);
+
if (pid != -1 && !(flags & PERF_FLAG_PID_CGROUP)) {
task = find_lively_task_by_vpid(pid);
if (IS_ERR(task)) {
@@ -6935,6 +6946,8 @@ SYSCALL_DEFINE5(perf_event_open,
++ctx->generation;
perf_unpin_context(ctx);
mutex_unlock(&ctx->mutex);
+ if (group_leader)
+ mutex_unlock(&group_leader->group_leader_mutex);
put_online_cpus();
@@ -6970,6 +6983,8 @@ err_task:
if (task)
put_task_struct(task);
err_group_fd:
+ if (group_leader)
+ mutex_unlock(&group_leader->group_leader_mutex);
fdput(group);
err_fd:
put_unused_fd(event_fd);