Commit 4a2c7a7837da1b91468e50426066d988050e4d56
Committed by
Linus Torvalds
1 parent
47e65328a7
Exists in
master
and in
4 other branches
[PATCH] make fork() atomic wrt pgrp/session signals
Eric W. Biederman wrote: > > Ok. SUSV3/Posix is clear, fork is atomic with respect > to signals. Either a signal comes before or after a > fork but not during. (See the rationale section). > http://www.opengroup.org/onlinepubs/000095399/functions/fork.html > > The tasklist_lock does not stop forks from adding to a process > group. The forks stall while the tasklist_lock is held, but a fork > that began before we grabbed the tasklist_lock simply completes > afterwards, and the child does not receive the signal. This also means that SIGSTOP or sig_kernel_coredump() signal can't be delivered to pgrp/session reliably. With this patch copy_process() returns -ERESTARTNOINTR when it detects a pending signal, fork() will be restarted transparently after handling the signals. This patch also deletes now unneeded "group_stop_count > 0" check, copy_process() can no longer succeed while group stop in progress. Signed-off-by: Oleg Nesterov <oleg@tv-sign.ru> Acked-By: Eric Biederman <ebiederm@xmission.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Showing 1 changed file with 17 additions and 20 deletions Side-by-side Diff
kernel/fork.c
... | ... | @@ -1136,16 +1136,6 @@ |
1136 | 1136 | !cpu_online(task_cpu(p)))) |
1137 | 1137 | set_task_cpu(p, smp_processor_id()); |
1138 | 1138 | |
1139 | - /* | |
1140 | - * Check for pending SIGKILL! The new thread should not be allowed | |
1141 | - * to slip out of an OOM kill. (or normal SIGKILL.) | |
1142 | - */ | |
1143 | - if (sigismember(¤t->pending.signal, SIGKILL)) { | |
1144 | - write_unlock_irq(&tasklist_lock); | |
1145 | - retval = -EINTR; | |
1146 | - goto bad_fork_cleanup_namespace; | |
1147 | - } | |
1148 | - | |
1149 | 1139 | /* CLONE_PARENT re-uses the old parent */ |
1150 | 1140 | if (clone_flags & (CLONE_PARENT|CLONE_THREAD)) |
1151 | 1141 | p->real_parent = current->real_parent; |
... | ... | @@ -1154,6 +1144,23 @@ |
1154 | 1144 | p->parent = p->real_parent; |
1155 | 1145 | |
1156 | 1146 | spin_lock(¤t->sighand->siglock); |
1147 | + | |
1148 | + /* | |
1149 | + * Process group and session signals need to be delivered to just the | |
1150 | + * parent before the fork or both the parent and the child after the | |
1151 | + * fork. Restart if a signal comes in before we add the new process to | |
1152 | + * it's process group. | |
1153 | + * A fatal signal pending means that current will exit, so the new | |
1154 | + * thread can't slip out of an OOM kill (or normal SIGKILL). | |
1155 | + */ | |
1156 | + recalc_sigpending(); | |
1157 | + if (signal_pending(current)) { | |
1158 | + spin_unlock(¤t->sighand->siglock); | |
1159 | + write_unlock_irq(&tasklist_lock); | |
1160 | + retval = -ERESTARTNOINTR; | |
1161 | + goto bad_fork_cleanup_namespace; | |
1162 | + } | |
1163 | + | |
1157 | 1164 | if (clone_flags & CLONE_THREAD) { |
1158 | 1165 | /* |
1159 | 1166 | * Important: if an exit-all has been started then |
... | ... | @@ -1169,16 +1176,6 @@ |
1169 | 1176 | |
1170 | 1177 | p->group_leader = current->group_leader; |
1171 | 1178 | list_add_tail_rcu(&p->thread_group, &p->group_leader->thread_group); |
1172 | - | |
1173 | - if (current->signal->group_stop_count > 0) { | |
1174 | - /* | |
1175 | - * There is an all-stop in progress for the group. | |
1176 | - * We ourselves will stop as soon as we check signals. | |
1177 | - * Make the new thread part of that group stop too. | |
1178 | - */ | |
1179 | - current->signal->group_stop_count++; | |
1180 | - set_tsk_thread_flag(p, TIF_SIGPENDING); | |
1181 | - } | |
1182 | 1179 | |
1183 | 1180 | if (!cputime_eq(current->signal->it_virt_expires, |
1184 | 1181 | cputime_zero) || |