提交 e597bc6a 编写于 作者: A Alexander Shishkin 提交者: Xie XiuQi

perf: Paper over the hw.target problems

euler inclusion
category: bugfix
bugzilla: 9513/11006
CVE: NA
--------------------------------------------------

[ Cheng Jian
HULK-Syzkaller reported a problem which has been reported
to mainline(lkml) by syzbot early, this patch comes from the
reply form lkml.
https://lkml.org/lkml/2019/2/28/529 ]

First, we have a race between perf_event_release_kernel() and
perf_free_event(), which happens when parent's event is released while the
child's fork fails (because of a fatal signal, for example), that looks
like this:

cpu X                            cpu Y
-----                            -----
                                 copy_process() error path
perf_release(parent)             +->perf_event_free_task()
+-> lock(child_ctx->mutex)       |  |
+-> remove_from_context(child)   |  |
+-> unlock(child_ctx->mutex)     |  |
|                                |  +-> lock(child_ctx->mutex)
|                                |  +-> unlock(child_ctx->mutex)
|                                +-> free_task(child_task)
+-> put_task_struct(child_task)

Technically, we're still holding a reference to the task via
parent->hw.target, that's not stopping free_task(), so we end up poking at
free'd memory, as is pointed out by KASAN in the syzkaller report (see Link
below). The straightforward fix is to drop the hw.target reference while
the task is still around.

Therein lies the second problem: the users of hw.target (uprobe) assume
that it's around at ->destroy() callback time, where they use it for
context. So, in order to not break the uprobe teardown and avoid leaking
stuff, we need to call ->destroy() at the same time.

This patch fixes the race and the subsequent fallout by doing both these
things at remove_from_context time.
Signed-off-by: NAlexander Shishkin <alexander.shishkin@linux.intel.com>
Link: https://syzkaller.appspot.com/bug?extid=a24c397a29ad22d86c98
Reported-by: syzbot+a24c397a29ad22d86c98@syzkaller.appspotmail.com
Signed-off-by: NCheng Jian <cj.chengjian@huawei.com>
Reviewed-by: NLi Bin <huawei.libin@huawei.com>
Signed-off-by: NYang Yingliang <yangyingliang@huawei.com>
上级 41c46e00
...@@ -2104,6 +2104,27 @@ static void perf_remove_from_context(struct perf_event *event, unsigned long fla ...@@ -2104,6 +2104,27 @@ static void perf_remove_from_context(struct perf_event *event, unsigned long fla
event_function_call(event, __perf_remove_from_context, (void *)flags); event_function_call(event, __perf_remove_from_context, (void *)flags);
/*
* This is as passable as any hw.target handling out there;
* hw.target implies task context, therefore, no migration.
* Which means that we can only get here at the teardown.
*/
if (event->hw.target) {
/*
* Now, the problem with, say uprobes, is that they
* use hw.target for context in their ->destroy()
* callbacks. Supposedly, they may need to poke at
* its contents, so better call it while we still
* have the task.
*/
if (event->destroy) {
event->destroy(event);
event->destroy = NULL;
}
put_task_struct(event->hw.target);
event->hw.target = NULL;
}
/* /*
* The above event_function_call() can NO-OP when it hits * The above event_function_call() can NO-OP when it hits
* TASK_TOMBSTONE. In that case we must already have been detached * TASK_TOMBSTONE. In that case we must already have been detached
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册