From: Oleg Nesterov de_thread() pre-allocates newsighand to make sure that exec() can't fail after killing all sub-threads. Imho, this buys nothing, but complicates the code: - this is (mostly) needed to handle CLONE_SIGHAND without CLONE_THREAD tasks, this is very unlikely (if ever used) case - unless we already have some serious problems, GFP_KERNEL allocation should not fail - ENOMEM still can happen after de_thread(), ->sighand is not the last object we have to allocate Change the code to allocate the new ->sighand on demand. Signed-off-by: Oleg Nesterov Cc: Roland McGrath Signed-off-by: Andrew Morton --- fs/exec.c | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff -puN fs/exec.c~exec-simplify-the-new-sighand-allocation fs/exec.c --- a/fs/exec.c~exec-simplify-the-new-sighand-allocation +++ a/fs/exec.c @@ -748,7 +748,7 @@ static int exec_mmap(struct mm_struct *m static int de_thread(struct task_struct *tsk) { struct signal_struct *sig = tsk->signal; - struct sighand_struct *newsighand, *oldsighand = tsk->sighand; + struct sighand_struct *oldsighand = tsk->sighand; spinlock_t *lock = &oldsighand->siglock; struct task_struct *leader = NULL; int count; @@ -763,10 +763,6 @@ static int de_thread(struct task_struct return 0; } - newsighand = kmem_cache_alloc(sighand_cachep, GFP_KERNEL); - if (!newsighand) - return -ENOMEM; - if (thread_group_empty(tsk)) goto no_thread_group; @@ -783,7 +779,6 @@ static int de_thread(struct task_struct */ spin_unlock_irq(lock); read_unlock(&tasklist_lock); - kmem_cache_free(sighand_cachep, newsighand); return -EAGAIN; } @@ -902,17 +897,16 @@ no_thread_group: if (leader) release_task(leader); - if (atomic_read(&oldsighand->count) == 1) { - /* - * Now that we nuked the rest of the thread group, - * it turns out we are not sharing sighand any more either. - * So we can just keep it. - */ - kmem_cache_free(sighand_cachep, newsighand); - } else { + if (atomic_read(&oldsighand->count) != 1) { + struct sighand_struct *newsighand; /* - * Move our state over to newsighand and switch it in. + * This ->sighand is shared with the CLONE_SIGHAND + * but not CLONE_THREAD task, switch to the new one. */ + newsighand = kmem_cache_alloc(sighand_cachep, GFP_KERNEL); + if (!newsighand) + return -ENOMEM; + atomic_set(&newsighand->count, 1); memcpy(newsighand->action, oldsighand->action, sizeof(newsighand->action)); _