From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752951AbdBMR2C (ORCPT ); Mon, 13 Feb 2017 12:28:02 -0500 Received: from mail-eopbgr30074.outbound.protection.outlook.com ([40.107.3.74]:37472 "EHLO EUR03-AM5-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1751007AbdBMR2A (ORCPT ); Mon, 13 Feb 2017 12:28:00 -0500 Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=mika.penttila@nextfour.com; Subject: Re: [PATCH 1/2] exec: don't wait for zombie threads with cred_guard_mutex held To: Oleg Nesterov , Andrew Morton References: <20170213141516.GA30233@redhat.com> CC: Aleksa Sarai , Andy Lutomirski , Attila Fazekas , "Eric W. Biederman" , Jann Horn , Kees Cook , Michal Hocko , Ulrich Obergfell , From: =?UTF-8?Q?Mika_Penttil=c3=a4?= Message-ID: <7ce94707-7bbe-a0d5-85a0-b93e73d76e22@nextfour.com> Date: Mon, 13 Feb 2017 19:27:51 +0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.7.0 MIME-Version: 1.0 In-Reply-To: <20170213141516.GA30233@redhat.com> Content-Type: text/plain; charset="windows-1252" Content-Transfer-Encoding: 7bit X-Originating-IP: [84.250.21.106] X-ClientProxiedBy: HE1PR09CA0089.eurprd09.prod.outlook.com (10.174.50.161) To DB6PR0701MB2168.eurprd07.prod.outlook.com (10.168.58.23) X-MS-Office365-Filtering-Correlation-Id: b4eddb90-3768-44cd-41a5-08d45435a657 X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:(22001);SRVR:DB6PR0701MB2168; X-Microsoft-Exchange-Diagnostics: 1;DB6PR0701MB2168;3:S+pXEyK4VmqoeDPptd3nCrI5N9mhFsSsinCpI6trpPmeNTZPhXZiatTE3NCelRueTZ4ul0V/MxaUlFJ6bbEDfzEm9j33SwERqDDAHuXAnEDuoIrS91z2kgOuyI3q3gmVMkfiEODSe9UBoxt5fxvYunKpxr0g2bCrGIwHP9Zc3AaH3dzR5mw5VxY6/ICKWSgujieKese6j8isvMFObP6/T6k9A2HglxIv2qeuc0XKQm7qraaovsbcmWi8nwcLXVRXv//wzCjEGMhqrZUvnd9YBA==;25:+Fdbh30Xh82k3rDlnomMxFRk/0Tdf8FswjEtn011OPA8gake8LyiO8sZ4gBBSPhFTzYt1C819Ki4MA6TvoKeArBAiWhe6Lbl3/C3SRPtSnCoGpF8rmgX9jidqI4O9z4LK+zF8RE0HAnWduclsK/AXCI67nshvuLJ2IjGULUg6i0UenNpXqQYHfXtD7roewwZUJJDpZMmqfPWj1xjUQGtZmhBZk/6s0nuLxIlh9tp626BE9DAmdB8sdJTJ5mfWMO9u9/xyNEoocUkgigycnIJT4pKXjmankzjreRYzGU4JjBoiVz7XC3jAxBFT2Jb6kaDnkPsvcjMVWKy1XwMosNbEuk36xAK4p91e7BuQmu5IBDUXFNTjTneEmZx7GlyT4yflBeJyk18WJLDaJ8CmVzTxb3Bd2xg8Mvlo7cgnfSS6jMea8Jpumr4THAZaivKdAUxqozJzcvUrmONcn6riO+ZLQ== X-Microsoft-Exchange-Diagnostics: 1;DB6PR0701MB2168;31:aQz11zKg1bXLoLaRu1ZeDRvEypRPtjiajOuF5TCp+RxLQZUYC2HvRhQZ+G61buqLOi5APr7jxzC/uDtu/wbmZQv//qcFejEcKLF9ps7n2VqJFFREFapLW8+LGu4+BRN3br0XqEBgL+o0isfkC2UDy7hJWEbCn5am86zAoIZhbBZs55RveSZSE0a2UgCJjroFdxTglOiQIEkjBk2zsjjGj7/ngdFZvrAcDLwA5avQ3UQf5AMX02Ung2jA185jLhSiXTCDXlrKqEAWYQfvbU4g0w== X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(6040375)(601004)(2401047)(8121501046)(5005006)(3002001)(10201501046)(6041248)(20161123555025)(20161123558025)(20161123564025)(2016111802025)(20161123562025)(20161123560025)(6072148)(6043046);SRVR:DB6PR0701MB2168;BCL:0;PCL:0;RULEID:;SRVR:DB6PR0701MB2168; X-Microsoft-Exchange-Diagnostics: 1;DB6PR0701MB2168;4:NxE0AJiSiM1bTzzwAFYCsxCJQU2F8IPmyJm+Pm1yiEwNIFY1wp8leiTJPzRBwxDOi5cXXRDNznNuTZwZqR7YeXm33jjtuba2lyXQYI/2utCEqIFRzTQwDlqsqDdfEDa3SJTwZUWbWxNGkSwmgjPJkppF6IvAZpeS9NVkGLR6O86EaGPCChWn7c+LkpjNjLoNEQgHpn5jbOSvXBldXfwqBEkU3XjxC3W7Rhm5n/snQc/yU9ce/VVtLDipBy96aojamSADwaY/AsnLF4Be+Q2i20JcKjU+unj3sSMjcwrFkPgHQP41YJmir1Ja8f0vxw/FnyBeDhQ32Ynt6qEv9QBrlrk6zh0tIhiaIvTItp6gYCU/mLrj4IP/P/gCLsn8UGiu+GeKeOBiisPqO0M5ErkX0D13ZV3cOOHTpp0GvxfWmhk3/GJp9p08qlTSxNNGA+ICX5MS/KbM6W6+gOhhGDU/P1Iy9JBSkWqvmy4YOUVJFAWAIPFlr89oM6gRyG1NspbH0O6eJr1JU3oHUd3wzrgPdTDU/8Q41pCuZbE+76KlLx2YBOTdWoXCShdCkdWtBSxh1tau3pXY+jVFsZbQMOZYspT0NkuzzwVCLV3MOvl4oiOxkyiSOBXTpOqF+fd/BsM/huBkNRKwlMoebrDQnNmC7619zmKUNd7zd4f5iPV2l3c= X-Forefront-PRVS: 02176E2458 X-Forefront-Antispam-Report: SFV:NSPM;SFS:(10009020)(4630300001)(6009001)(6049001)(7916002)(39450400003)(199003)(24454002)(189002)(36756003)(6666003)(2950100002)(4001350100001)(105586002)(50986999)(101416001)(42186005)(76176999)(106356001)(54356999)(50466002)(31686004)(189998001)(25786008)(6486002)(81166006)(81156014)(77096006)(64126003)(8676002)(230700001)(90366009)(33646002)(68736007)(65826007)(5660300001)(7736002)(117156001)(97736004)(31696002)(229853002)(66066001)(65806001)(53936002)(2906002)(23746002)(38730400002)(65956001)(54906002)(47776003)(83506001)(86362001)(6246003)(53546003)(7416002)(92566002)(4326007)(575784001)(305945005)(6116002)(3846002);DIR:OUT;SFP:1101;SCL:1;SRVR:DB6PR0701MB2168;H:[192.168.1.71];FPR:;SPF:None;PTR:InfoNoRecords;A:1;MX:1;LANG:en; X-Microsoft-Exchange-Diagnostics: =?Windows-1252?Q?1;DB6PR0701MB2168;23:01reiFWFQfdJaRC5x7beKFoUCIGAIMkexUC?= =?Windows-1252?Q?E7IB3jE38B5jLVlQN/WKNdBafUVnJaZh3VVnoTaCN+6jiisXN2cGSV+t?= =?Windows-1252?Q?bqL6yPdY/uKHUAhalwLcTUawfBYo/MpDZ03J7wKKqkyfj4Q35Jc239ge?= =?Windows-1252?Q?Rh2ELGWxeSh0G5fWxaPf8LeRmw8GlwxVjSa1wbXwZQAdrliyHQzYPnEk?= =?Windows-1252?Q?GRUlWqV0gaOdZp3CWpo58hHJmGvFFIFlmkt9xCO2DljCzLNP+WhnA3sz?= =?Windows-1252?Q?iKsFL1DccXy4y9Lxu+tiLiJypCm8eoJppVD9a1fMEJ14oX3iUjE6Qat8?= =?Windows-1252?Q?kVfNOUuoMwi++jGLmG2PHV4JAHDvdQwaabnWeGJsaQqlPIysfjBfLpBN?= =?Windows-1252?Q?r2x68m04FFFQjip4aGU6ZyPQiiyHw3GbtnyTIcHyBnPYXQfF/7AhknVW?= =?Windows-1252?Q?iSvkgvZK05aqQ9/oR/ZRwSIXpSAo3lFhzdWpRYfZ1OVPewMIpp0+l+gb?= =?Windows-1252?Q?zNEQEVgIuq4iaToUGEqB/cpc2R+Bz8hMwjy+l3dXNu7aKYXjMU0WckMs?= =?Windows-1252?Q?jrr4hs54tzQR6cIC0vUb4vaQGorGgkRvr+5H00b9fEeGQLv1CkzVBjlD?= =?Windows-1252?Q?N/xdA81yE9M6G+mFwwwMIbW3CSk6Pq4lzn9iRgJQQ7Uo507T3CjUgwMN?= =?Windows-1252?Q?+vgJkhhdrigdD6P/yLBtqCVoRJe0r4LKr+uZM5LxGM2tWy/XUzyDhfaE?= =?Windows-1252?Q?tv10P4jRH+/T6nJ/Bx1Ben+Uy7tQnwhclKJAWwExZmeR+brmVf1DxuZG?= =?Windows-1252?Q?Jt5cuu6chswNx8IszwPkbOkkbENJ0ffKKnmSPaoOFlGt8etTlN6gfCY7?= =?Windows-1252?Q?w02mQ2J9NwJoFcCC6exjchRnBuMtxcWwdxx7XnCKPnQ/ixMHtRzs+XnX?= =?Windows-1252?Q?1GWD5kiLm4YrZ//k+ET/mBP5+i3NgOOl1Y7u1qplLllgeemXJuEe4dT4?= =?Windows-1252?Q?7fVrh4wYx+8UuTSN5wPwa7vDBhPTuS9Lj7iGzhWditbLmaZwEEjIqHux?= =?Windows-1252?Q?gii8fIqQn+46AlFSEb1cFo6H3rNHxTiAlCrKlrDrLEw0rK7NiYx8LCrY?= =?Windows-1252?Q?GG+nPnANI1VXwBNhWFVWZBSeR7mluU6yw8Zv8HHHmaHno79NGVW6Ljt6?= =?Windows-1252?Q?ebUQ/5P0qpVUYXV/BbsY/3NpThUKCCfPS/re+AB5uqXB65783tP6QO+R?= =?Windows-1252?Q?uGKaVYQVp9lJFbSGQDjAbIHjVH1HHeuUqYxVbipgwH2Uyhgbsv6Omklm?= =?Windows-1252?Q?UnUqoFR22R92Dz2FqZq8lHzSNjm8AbzYhyU1/IrZv+//XqHYXmHvVdb1?= =?Windows-1252?Q?69sSqE0eYEssKnnzc0t9ldty7xKD3koYG5vGcWmhx63RwaAywqL0C6mK?= =?Windows-1252?Q?4/N1tPLplMAjJZorsSP+Gc6Kab3gBSkv2lqpEnoxkyRCjfdRS7+xou8X?= =?Windows-1252?Q?JSMmun6w+8rBLj5RRV1OCfJTtxYqUUY0HPSRAFuJJE8K+qt8R1Q=3D?= =?Windows-1252?Q?=3D?= X-Microsoft-Exchange-Diagnostics: 1;DB6PR0701MB2168;6:GY9tcXLp5ui+L/rf6fEoTnSqpCq5nQoPlpM9k1qBqI4G2Gh4YG5Ml+DkS5x7tJ+2YsCUPx0IKYYZcDaGvM8YC0UrWD0I4Anr8QrZl882KY1rREDMb/ekoI63dref+sXmn22tSYv9wOM3KXqWRd4SWKtybdyNXkbqZi6tVidHpuUWBYlJolLJiHtLoBMEEvscAmVGwOa2IGNVbk2Rq08yJMShdLBM0J3ojCKFeP7bSd8q3yWknZ0eFTjdXdG38Cm4qzTR6LNnTdp3yBrbl+y/styRZbbZM71BnmRtn1JCt8nT2V/t57c9B5U/CCqw5AieCnQwYRL/bBm69BPVjTyC6Fuod7OIn63KnDBsvY96knbC3h+uXF1DIw1iycYphmMjCAzRAIJI3YjpzBXKqQoogA==;5:o5sMV443OqU2XMaDmofex39a2PUA4n8aLWnBmS7RhAPjU8SmiMQ/0+nLEKK/3Ehg3Hs+1G10aXkq8RIx4wXQnrxtS0rC3YFD5wVZf4AqbbvLc/4h5aRL+FHY3usvzIG0WV9RIfhto6wctROhsT+lAg==;24:nLNrsSOH7NFtLDUYRb8/hLgAodV8y+9Jhj5edTfgZGkzjWhys/ntrBjhArIQvvCbrsstaSKVA9aMR2Y9xYNokI7cslm9LF/n8s+WF03iOns= SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-Microsoft-Exchange-Diagnostics: 1;DB6PR0701MB2168;7:AkdIvXw83z/Zxoyd6Kix4US0QN0tpIrCWo527umHnOrBUuM2mUIOJugJPWZVQX1n7ujU4g0ErVXzhOaADjN2tQvXwEG312pZ7NZtFFNriV3Hfmsy1z30BYSgV/b5XWJs+VLQSHOUAGQXd0mDfcYOmQgoMGRprmRnnrj5sw5Q/IG3ZPOcUNbyqqCo/4iLYjH+m76/2rTbCk/f5ghlj8Kx5UJTVK3BLMwTEvuYr1XTmZrT+cJ6BZhlBa3GK0Q9Bpup4dKFMQl1TOu0QpFJVlM4rheRp/RNSDAx6FZxTcBdz317Np3Iis4oN+rVcMruI8wOfd3MbG1Bq/+0+3wuy13AGCoNi9325l26lGJF4nrh2MlE/cZ7IVk/Z2bTZNXS5FIXhbOVvRqSBhrNy4vjp6NNujVOx5vVIkgGZxmF8IDvN5yjp8vsSBhbZlDa9GKh7Yp7AzkPUJz+1dBbjzcaHDnfsEfiR/jDnA3n0lJvwrpMUEy8yV2prmBvaqEfQ6sToX/7Bo44IkokvfRIR8hrNYOI3g== X-OriginatorOrg: nextfour.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 13 Feb 2017 17:27:55.7550 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB6PR0701MB2168 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On 13.02.2017 16:15, Oleg Nesterov wrote: > + retval = de_thread(current); > + if (retval) > + return retval; > > if (N_MAGIC(ex) == OMAGIC) { > unsigned long text_addr, map_size; > diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c > index 4223702..79508f7 100644 > --- a/fs/binfmt_elf.c > +++ b/fs/binfmt_elf.c > @@ -855,13 +855,17 @@ static int load_elf_binary(struct linux_binprm *bprm) > setup_new_exec(bprm); > install_exec_creds(bprm); > > + retval = de_thread(current); > + if (retval) > + goto out_free_dentry; > + > /* Do this so that we can load the interpreter, if need be. We will > change some of these later */ > retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP), > executable_stack); > if (retval < 0) > goto out_free_dentry; > - > + > current->mm->start_stack = bprm->p; > > /* Now we do a little grungy work by mmapping the ELF image into > diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c > index d2e36f8..75fd6d8 100644 > --- a/fs/binfmt_elf_fdpic.c > +++ b/fs/binfmt_elf_fdpic.c > @@ -430,6 +430,10 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm) > #endif > > install_exec_creds(bprm); > + retval = de_thread(current); > + if (retval) > + goto error; > + > if (create_elf_fdpic_tables(bprm, current->mm, > &exec_params, &interp_params) < 0) > goto error; > diff --git a/fs/binfmt_flat.c b/fs/binfmt_flat.c > index 9b2917a..a0ad9a3 100644 > --- a/fs/binfmt_flat.c > +++ b/fs/binfmt_flat.c > @@ -953,6 +953,9 @@ static int load_flat_binary(struct linux_binprm *bprm) > } > > install_exec_creds(bprm); > + res = de_thread(current); > + if (res) > + return res; > > set_binfmt(&flat_format); > > diff --git a/fs/exec.c b/fs/exec.c > index e579466..8591c56 100644 > --- a/fs/exec.c > +++ b/fs/exec.c > @@ -1036,13 +1036,62 @@ static int exec_mmap(struct mm_struct *mm) > return 0; > } > > +static int wait_for_notify_count(struct task_struct *tsk, struct signal_struct *sig) > +{ > + for (;;) { > + if (unlikely(__fatal_signal_pending(tsk))) > + goto killed; > + set_current_state(TASK_KILLABLE); > + if (!sig->notify_count) > + break; > + schedule(); > + } > + __set_current_state(TASK_RUNNING); > + return 0; > + > +killed: > + /* protects against exit_notify() and __exit_signal() */ > + read_lock(&tasklist_lock); > + sig->group_exit_task = NULL; > + sig->notify_count = 0; > + read_unlock(&tasklist_lock); > + return -EINTR; > +} > + > +/* > + * Kill all the sub-threads and wait until they all pass exit_notify(). > + */ > +static int kill_sub_threads(struct task_struct *tsk) > +{ > + struct signal_struct *sig = tsk->signal; > + int err = -EINTR; > + > + if (thread_group_empty(tsk)) > + return 0; > + > + read_lock(&tasklist_lock); > + spin_lock_irq(&tsk->sighand->siglock); > + if (!signal_group_exit(sig)) { > + sig->group_exit_task = tsk; > + sig->notify_count = -zap_other_threads(tsk); > + err = 0; > + } > + spin_unlock_irq(&tsk->sighand->siglock); > + read_unlock(&tasklist_lock); > + > + if (!err) > + err = wait_for_notify_count(tsk, sig); > + return err; > + > +} > + > /* > - * This function makes sure the current process has its own signal table, > - * so that flush_signal_handlers can later reset the handlers without > - * disturbing other processes. (Other processes might share the signal > - * table via the CLONE_SIGHAND option to clone().) > + * This function makes sure the current process has no other threads and > + * has a private signal table so that flush_signal_handlers() can reset > + * the handlers without disturbing other processes which might share the > + * signal table via the CLONE_SIGHAND option to clone(). > */ > -static int de_thread(struct task_struct *tsk) > +int de_thread(struct task_struct *tsk) > { > struct signal_struct *sig = tsk->signal; > struct sighand_struct *oldsighand = tsk->sighand; > @@ -1051,60 +1100,24 @@ static int de_thread(struct task_struct *tsk) > if (thread_group_empty(tsk)) > goto no_thread_group; > > - /* > - * Kill all other threads in the thread group. > - */ > spin_lock_irq(lock); > - if (signal_group_exit(sig)) { > - /* > - * Another group action in progress, just > - * return so that the signal is processed. > - */ > - spin_unlock_irq(lock); > - return -EAGAIN; > - } > - > - sig->group_exit_task = tsk; > - sig->notify_count = zap_other_threads(tsk); > + sig->notify_count = sig->nr_threads; maybe nr_threads - 1 since nr_threads includes us ? + sig->notify_count = sig->nr_threads - 1; > if (!thread_group_leader(tsk)) > sig->notify_count--; > - > - while (sig->notify_count) { > - __set_current_state(TASK_KILLABLE); > - spin_unlock_irq(lock); > - schedule(); > - if (unlikely(__fatal_signal_pending(tsk))) > - goto killed; > - spin_lock_irq(lock); > - } > spin_unlock_irq(lock); > > + if (wait_for_notify_count(tsk, sig)) > + return -EINTR; > + > /* > * At this point all other threads have exited, all we have to > - * do is to wait for the thread group leader to become inactive, > - * and to assume its PID: > + * do is to reap the old leader and assume its PID. > */ > if (!thread_group_leader(tsk)) { > struct task_struct *leader = tsk->group_leader; > > - for (;;) { > - threadgroup_change_begin(tsk); > - write_lock_irq(&tasklist_lock); > - /* > - * Do this under tasklist_lock to ensure that > - * exit_notify() can't miss ->group_exit_task > - */ > - sig->notify_count = -1; > - if (likely(leader->exit_state)) > - break; > - __set_current_state(TASK_KILLABLE); > - write_unlock_irq(&tasklist_lock); > - threadgroup_change_end(tsk); > - schedule(); > - if (unlikely(__fatal_signal_pending(tsk))) > - goto killed; > - } > - > + threadgroup_change_begin(tsk); > + write_lock_irq(&tasklist_lock); > /* > * The only record we have of the real-time age of a > * process, regardless of execs it's done, is start_time. > @@ -1162,10 +1175,9 @@ static int de_thread(struct task_struct *tsk) > release_task(leader); > } > > +no_thread_group: > sig->group_exit_task = NULL; > sig->notify_count = 0; > - > -no_thread_group: > /* we have changed execution domain */ > tsk->exit_signal = SIGCHLD; > > @@ -1198,15 +1210,8 @@ static int de_thread(struct task_struct *tsk) > } > > BUG_ON(!thread_group_leader(tsk)); > + flush_signal_handlers(current, 0); > return 0; > - > -killed: > - /* protects against exit_notify() and __exit_signal() */ > - read_lock(&tasklist_lock); > - sig->group_exit_task = NULL; > - sig->notify_count = 0; > - read_unlock(&tasklist_lock); > - return -EAGAIN; > } > > char *get_task_comm(char *buf, struct task_struct *tsk) > @@ -1237,11 +1242,7 @@ int flush_old_exec(struct linux_binprm * bprm) > { > int retval; > > - /* > - * Make sure we have a private signal table and that > - * we are unassociated from the previous thread group. > - */ > - retval = de_thread(current); > + retval = kill_sub_threads(current); > if (retval) > goto out; > > @@ -1336,7 +1337,6 @@ void setup_new_exec(struct linux_binprm * bprm) > /* An exec changes our domain. We are no longer part of the thread > group */ > current->self_exec_id++; > - flush_signal_handlers(current, 0); > } > EXPORT_SYMBOL(setup_new_exec); > > diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h > index 1303b57..06a5a7b 100644 > --- a/include/linux/binfmts.h > +++ b/include/linux/binfmts.h > @@ -101,6 +101,7 @@ extern int __must_check remove_arg_zero(struct linux_binprm *); > extern int search_binary_handler(struct linux_binprm *); > extern int flush_old_exec(struct linux_binprm * bprm); > extern void setup_new_exec(struct linux_binprm * bprm); > +extern int de_thread(struct task_struct *tsk); > extern void would_dump(struct linux_binprm *, struct file *); > > extern int suid_dumpable; > diff --git a/kernel/exit.c b/kernel/exit.c > index 8f14b86..169d9f2 100644 > --- a/kernel/exit.c > +++ b/kernel/exit.c > @@ -699,8 +699,9 @@ static void exit_notify(struct task_struct *tsk, int group_dead) > if (tsk->exit_state == EXIT_DEAD) > list_add(&tsk->ptrace_entry, &dead); > > - /* mt-exec, de_thread() is waiting for group leader */ > - if (unlikely(tsk->signal->notify_count < 0)) > + /* mt-exec, kill_sub_threads() is waiting for group exit */ > + if (unlikely(tsk->signal->notify_count < 0) && > + !++tsk->signal->notify_count) > wake_up_process(tsk->signal->group_exit_task); > write_unlock_irq(&tasklist_lock); > > diff --git a/kernel/signal.c b/kernel/signal.c > index 3603d93..b78ce63 100644 > --- a/kernel/signal.c > +++ b/kernel/signal.c > @@ -1200,13 +1200,12 @@ int zap_other_threads(struct task_struct *p) > > while_each_thread(p, t) { > task_clear_jobctl_pending(t, JOBCTL_PENDING_MASK); > - count++; > - > /* Don't bother with already dead threads */ > if (t->exit_state) > continue; > sigaddset(&t->pending.signal, SIGKILL); > signal_wake_up(t, 1); > + count++; > } > > return count;