diff options
author | Oleg Nesterov <oleg@redhat.com> | 2013-04-30 18:28:12 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-04-30 20:04:06 -0400 |
commit | 6cd8f0acae3420afce37bf51a9ff8c2c20342af5 (patch) | |
tree | 38478d4eb5b1c2d911c865c088a79edf2ebcea77 /fs/coredump.c | |
parent | 403bad72b67d8b3f5a0240af5023adfa48132a65 (diff) |
coredump: ensure that SIGKILL always kills the dumping thread
prepare_signal() blesses SIGKILL sent to the dumping process but this
signal can be "lost" anyway. The problems is, complete_signal() sees
SIGNAL_GROUP_EXIT and skips the "kill them all" logic. And even if the
dumping process is single-threaded (so the target is always "correct"),
the group-wide SIGKILL is not recorded in task->pending and thus
__fatal_signal_pending() won't be true. A multi-threaded case has even
more problems.
And even ignoring all technical details, SIGNAL_GROUP_EXIT doesn't look
right to me. This coredumping process is not exiting yet, it can do a lot
of work dumping the core.
With this patch the dumping process doesn't have SIGNAL_GROUP_EXIT, we set
signal->group_exit_task instead. This makes signal_group_exit() true and
thus this should equally close the races with exit/exec/stop but allows to
kill the dumping thread reliably.
Notes:
- It is not clear what should we do with ->group_exit_code
if the dumper was killed, see the next change.
- we need more (hopefully straightforward) changes to ensure
that SIGKILL actually interrupts the coredump. Basically we
need to check __fatal_signal_pending() in dump_write() and
dump_seek().
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Tested-by: Mandeep Singh Baines <msb@chromium.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Neil Horman <nhorman@redhat.com>
Cc: "Rafael J. Wysocki" <rjw@sisk.pl>
Cc: Roland McGrath <roland@hack.frob.com>
Cc: Tejun Heo <tj@kernel.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/coredump.c')
-rw-r--r-- | fs/coredump.c | 10 |
1 files changed, 8 insertions, 2 deletions
diff --git a/fs/coredump.c b/fs/coredump.c index f91cfd8cd5f2..6fea59043532 100644 --- a/fs/coredump.c +++ b/fs/coredump.c | |||
@@ -263,7 +263,6 @@ static int zap_process(struct task_struct *start, int exit_code) | |||
263 | struct task_struct *t; | 263 | struct task_struct *t; |
264 | int nr = 0; | 264 | int nr = 0; |
265 | 265 | ||
266 | start->signal->flags = SIGNAL_GROUP_EXIT; | ||
267 | start->signal->group_exit_code = exit_code; | 266 | start->signal->group_exit_code = exit_code; |
268 | start->signal->group_stop_count = 0; | 267 | start->signal->group_stop_count = 0; |
269 | 268 | ||
@@ -291,8 +290,9 @@ static int zap_threads(struct task_struct *tsk, struct mm_struct *mm, | |||
291 | if (!signal_group_exit(tsk->signal)) { | 290 | if (!signal_group_exit(tsk->signal)) { |
292 | mm->core_state = core_state; | 291 | mm->core_state = core_state; |
293 | nr = zap_process(tsk, exit_code); | 292 | nr = zap_process(tsk, exit_code); |
293 | tsk->signal->group_exit_task = tsk; | ||
294 | /* ignore all signals except SIGKILL, see prepare_signal() */ | 294 | /* ignore all signals except SIGKILL, see prepare_signal() */ |
295 | tsk->signal->flags |= SIGNAL_GROUP_COREDUMP; | 295 | tsk->signal->flags = SIGNAL_GROUP_COREDUMP; |
296 | clear_tsk_thread_flag(tsk, TIF_SIGPENDING); | 296 | clear_tsk_thread_flag(tsk, TIF_SIGPENDING); |
297 | } | 297 | } |
298 | spin_unlock_irq(&tsk->sighand->siglock); | 298 | spin_unlock_irq(&tsk->sighand->siglock); |
@@ -343,6 +343,7 @@ static int zap_threads(struct task_struct *tsk, struct mm_struct *mm, | |||
343 | if (unlikely(p->mm == mm)) { | 343 | if (unlikely(p->mm == mm)) { |
344 | lock_task_sighand(p, &flags); | 344 | lock_task_sighand(p, &flags); |
345 | nr += zap_process(p, exit_code); | 345 | nr += zap_process(p, exit_code); |
346 | p->signal->flags = SIGNAL_GROUP_EXIT; | ||
346 | unlock_task_sighand(p, &flags); | 347 | unlock_task_sighand(p, &flags); |
347 | } | 348 | } |
348 | break; | 349 | break; |
@@ -394,6 +395,11 @@ static void coredump_finish(struct mm_struct *mm) | |||
394 | struct core_thread *curr, *next; | 395 | struct core_thread *curr, *next; |
395 | struct task_struct *task; | 396 | struct task_struct *task; |
396 | 397 | ||
398 | spin_lock_irq(¤t->sighand->siglock); | ||
399 | current->signal->group_exit_task = NULL; | ||
400 | current->signal->flags = SIGNAL_GROUP_EXIT; | ||
401 | spin_unlock_irq(¤t->sighand->siglock); | ||
402 | |||
397 | next = mm->core_state->dumper.next; | 403 | next = mm->core_state->dumper.next; |
398 | while ((curr = next) != NULL) { | 404 | while ((curr = next) != NULL) { |
399 | next = curr->next; | 405 | next = curr->next; |