From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752739AbdCNPDL (ORCPT ); Tue, 14 Mar 2017 11:03:11 -0400 Received: from bombadil.infradead.org ([65.50.211.133]:55450 "EHLO bombadil.infradead.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750805AbdCNPC4 (ORCPT ); Tue, 14 Mar 2017 11:02:56 -0400 Date: Tue, 14 Mar 2017 16:02:41 +0100 From: Peter Zijlstra To: Oleg Nesterov Cc: Dmitry Vyukov , Ingo Molnar , Arnaldo Carvalho de Melo , Alexander Shishkin , LKML , Mathieu Desnoyers , syzkaller Subject: Re: perf: use-after-free in perf_release Message-ID: <20170314150241.GO5680@worktop> References: <20170306131459.GC6515@twins.programming.kicks-ass.net> <20170307140414.GA31678@redhat.com> <20170307165131.GA6097@redhat.com> <20170314125508.GK3343@twins.programming.kicks-ass.net> <20170314140302.GA28146@redhat.com> <20170314140754.GG3328@twins.programming.kicks-ass.net> <20170314143010.GA30351@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20170314143010.GA30351@redhat.com> User-Agent: Mutt/1.5.22.1 (2013-10-16) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Tue, Mar 14, 2017 at 03:30:11PM +0100, Oleg Nesterov wrote: > But. perf_event_init_task() adds child_event to parent_event->child_list. > > If perf_event_release_kernel(parent_event) is called before copy_process() > does perf_event_free_task() which (in particular) removes it from child_list, > perf_event_release_kernel() can find this child_event and do get_ctx(ctx) > (under the list_for_each_entry(child, &event->child_list, child_list) loop). Right; the child_list is the only thing that is exposed. And yes, it looks like that can interleave just right. > Then it does put_ctx(ctx), but ctx->task can be already freed by > copy_process()->free_task() in this case. Task1 Task2 fork() perf_event_init_task() /* ... */ goto bad_fork_$foo; /* ... */ perf_event_free_task() mutex_lock(ctx->lock) perf_free_event(B) perf_event_release_kernel(A) mutex_lock(A->child_mutex) list_for_each_entry(child, ...) { /* child == B */ ctx = B->ctx; get_ctx(ctx); mutex_unlock(A->child_mutex); mutex_lock(A->child_mutex) list_del_init(B->child_list) mutex_unlock(A->child_mutex) /* ... */ mutex_unlock(ctx->lock); put_ctx() /* >0 */ free_task(); mutex_lock(ctx->lock); mutex_lock(A->child_mutex); /* ... */ mutex_unlock(A->child_mutex); mutex_unlock(ctx->lock) put_ctx() /* 0 */ ctx->task && !TOMBSTONE put_task_struct() /* UAF */ Something like that, right? Let me see if it makes sense to retain perf_event_free_task() at all; maybe we should always do perf_event_exit_task().