Re: uprobes/perf: KASAN: use-after-free in uprobe_perf_close

From: Oleg Nesterov
Date: Thu Feb 22 2018 - 12:50:08 EST


On 02/22, Peter Zijlstra wrote:
>
> On Thu, Feb 22, 2018 at 05:37:15PM +0100, Oleg Nesterov wrote:
> > On 02/22, Prashant Bhole wrote:
>
> > > After debugging, found that uprobe_perf_close() is called after task has
> > > been terminated and uprobe_perf_close() tries to access task_struct of the
> > > terminated process.
> >
> > Oh. You can't imagine how much I forgot this code ;) I will recheck, but at
> > first glance you are right. We can't rely on _free_event()->put_ctx() which
> > does put_task_struct() after event->destroy(), the exiting task does
> > put_task_struct(current) itself and sets child_ctx->task = TASK_TOMBSTONE in
> > perf_event_exit_task_context().
> >
> > In short, nothing protects event->hw.target. But uprobe_perf_open() should be
> > safe, perf_init_event() is called when the caller has the additional reference.
> >
> > I am wondering if this was wrong from the very beginning or it was broken later,
> > but I won't even try to check.
>
> b2fe8ba674e8 ("uprobes/perf: Avoid uprobe_apply() whenever possible")
>
> Seems to have added that PF_EXITING test that dereferences the target
> pointer.

Hehe ;) no, I think we should blame another commit 63b6da39bb38e8f1a1ef3180d32a39d6
("perf: Fix perf_event_exit_task() race").

I can be easily wrong, but after perf_event_exit_task_context()->put_task_struct()
added by this commit nothing protects event->hw.target.

And just in case, we can simply remove that PF_EXITING test in uprobe_perf_close(),
this is a minor optimization. But __uprobe_perf_filter() needs a stable ->target too.

Oleg.