Skip to content

Commit

Permalink
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel…
Browse files Browse the repository at this point in the history
…/git/ebiederm/user-namespace

Pull signal fixes from Eric Biederman:
 "This contains four small fixes for signal handling. A missing range
  check, a regression fix, prioritizing signals we have already started
  a signal group exit for, and better detection of synchronous signals.

  The confused decision of which signals to handle failed spectacularly
  when a timer was pointed at SIGBUS and the stack overflowed. Resulting
  in an unkillable process in an infinite loop instead of a SIGSEGV and
  core dump"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace:
  signal: Better detection of synchronous signals
  signal: Always notice exiting tasks
  signal: Always attempt to allocate siginfo for SIGSTOP
  signal: Make siginmask safe when passed a signal of 0
  • Loading branch information
torvalds committed Feb 8, 2019
2 parents 3b6e820 + 7146db3 commit 6b2912c
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 5 deletions.
2 changes: 1 addition & 1 deletion include/linux/signal.h
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ extern bool unhandled_signal(struct task_struct *tsk, int sig);
#endif

#define siginmask(sig, mask) \
((sig) < SIGRTMIN && (rt_sigmask(sig) & (mask)))
((sig) > 0 && (sig) < SIGRTMIN && (rt_sigmask(sig) & (mask)))

#define SIG_KERNEL_ONLY_MASK (\
rt_sigmask(SIGKILL) | rt_sigmask(SIGSTOP))
Expand Down
63 changes: 59 additions & 4 deletions kernel/signal.c
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,48 @@ int dequeue_signal(struct task_struct *tsk, sigset_t *mask, kernel_siginfo_t *in
}
EXPORT_SYMBOL_GPL(dequeue_signal);

static int dequeue_synchronous_signal(kernel_siginfo_t *info)
{
struct task_struct *tsk = current;
struct sigpending *pending = &tsk->pending;
struct sigqueue *q, *sync = NULL;

/*
* Might a synchronous signal be in the queue?
*/
if (!((pending->signal.sig[0] & ~tsk->blocked.sig[0]) & SYNCHRONOUS_MASK))
return 0;

/*
* Return the first synchronous signal in the queue.
*/
list_for_each_entry(q, &pending->list, list) {
/* Synchronous signals have a postive si_code */
if ((q->info.si_code > SI_USER) &&
(sigmask(q->info.si_signo) & SYNCHRONOUS_MASK)) {
sync = q;
goto next;
}
}
return 0;
next:
/*
* Check if there is another siginfo for the same signal.
*/
list_for_each_entry_continue(q, &pending->list, list) {
if (q->info.si_signo == sync->info.si_signo)
goto still_pending;
}

sigdelset(&pending->signal, sync->info.si_signo);
recalc_sigpending();
still_pending:
list_del_init(&sync->list);
copy_siginfo(info, &sync->info);
__sigqueue_free(sync);
return info->si_signo;
}

/*
* Tell a process that it has a new active signal..
*
Expand Down Expand Up @@ -1057,10 +1099,9 @@ static int __send_signal(int sig, struct kernel_siginfo *info, struct task_struc

result = TRACE_SIGNAL_DELIVERED;
/*
* Skip useless siginfo allocation for SIGKILL SIGSTOP,
* and kernel threads.
* Skip useless siginfo allocation for SIGKILL and kernel threads.
*/
if (sig_kernel_only(sig) || (t->flags & PF_KTHREAD))
if ((sig == SIGKILL) || (t->flags & PF_KTHREAD))
goto out_set;

/*
Expand Down Expand Up @@ -2394,6 +2435,11 @@ bool get_signal(struct ksignal *ksig)
goto relock;
}

/* Has this task already been marked for death? */
ksig->info.si_signo = signr = SIGKILL;
if (signal_group_exit(signal))
goto fatal;

for (;;) {
struct k_sigaction *ka;

Expand All @@ -2407,7 +2453,15 @@ bool get_signal(struct ksignal *ksig)
goto relock;
}

signr = dequeue_signal(current, &current->blocked, &ksig->info);
/*
* Signals generated by the execution of an instruction
* need to be delivered before any other pending signals
* so that the instruction pointer in the signal stack
* frame points to the faulting instruction.
*/
signr = dequeue_synchronous_signal(&ksig->info);
if (!signr)
signr = dequeue_signal(current, &current->blocked, &ksig->info);

if (!signr)
break; /* will return 0 */
Expand Down Expand Up @@ -2489,6 +2543,7 @@ bool get_signal(struct ksignal *ksig)
continue;
}

fatal:
spin_unlock_irq(&sighand->siglock);

/*
Expand Down

0 comments on commit 6b2912c

Please sign in to comment.