Skip to content

Commit 43347d5

Browse files
mirabJiri Kosina
authored andcommitted
livepatch: send a fake signal to all blocking tasks
Live patching consistency model is of LEAVE_PATCHED_SET and SWITCH_THREAD. This means that all tasks in the system have to be marked one by one as safe to call a new patched function. Safe means when a task is not (sleeping) in a set of patched functions. That is, no patched function is on the task's stack. Another clearly safe place is the boundary between kernel and userspace. The patching waits for all tasks to get outside of the patched set or to cross the boundary. The transition is completed afterwards. The problem is that a task can block the transition for quite a long time, if not forever. It could sleep in a set of patched functions, for example. Luckily we can force the task to leave the set by sending it a fake signal, that is a signal with no data in signal pending structures (no handler, no sign of proper signal delivered). Suspend/freezer use this to freeze the tasks as well. The task gets TIF_SIGPENDING set and is woken up (if it has been sleeping in the kernel before) or kicked by rescheduling IPI (if it was running on other CPU). This causes the task to go to kernel/userspace boundary where the signal would be handled and the task would be marked as safe in terms of live patching. There are tasks which are not affected by this technique though. The fake signal is not sent to kthreads. They should be handled differently. They can be woken up so they leave the patched set and their TIF_PATCH_PENDING can be cleared thanks to stack checking. For the sake of completeness, if the task is in TASK_RUNNING state but not currently running on some CPU it doesn't get the IPI, but it would eventually handle the signal anyway. Second, if the task runs in the kernel (in TASK_RUNNING state) it gets the IPI, but the signal is not handled on return from the interrupt. It would be handled on return to the userspace in the future when the fake signal is sent again. Stack checking deals with these cases in a better way. If the task was sleeping in a syscall it would be woken by our fake signal, it would check if TIF_SIGPENDING is set (by calling signal_pending() predicate) and return ERESTART* or EINTR. Syscalls with ERESTART* return values are restarted in case of the fake signal (see do_signal()). EINTR is propagated back to the userspace program. This could disturb the program, but... * each process dealing with signals should react accordingly to EINTR return values. * syscalls returning EINTR happen to be quite common situation in the system even if no fake signal is sent. * freezer sends the fake signal and does not deal with EINTR anyhow. Thus EINTR values are returned when the system is resumed. The very safe marking is done in architectures' "entry" on syscall and interrupt/exception exit paths, and in a stack checking functions of livepatch. TIF_PATCH_PENDING is cleared and the next recalc_sigpending() drops TIF_SIGPENDING. In connection with this, also call klp_update_patch_state() before do_signal(), so that recalc_sigpending() in dequeue_signal() can clear TIF_PATCH_PENDING immediately and thus prevent a double call of do_signal(). Note that the fake signal is not sent to stopped/traced tasks. Such task prevents the patching to finish till it continues again (is not traced anymore). Last, sending the fake signal is not automatic. It is done only when admin requests it by writing 1 to signal sysfs attribute in livepatch sysfs directory. Signed-off-by: Miroslav Benes <[email protected]> Cc: Oleg Nesterov <[email protected]> Cc: Michael Ellerman <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: "H. Peter Anvin" <[email protected]> Cc: Andy Lutomirski <[email protected]> Cc: [email protected] Cc: [email protected] Acked-by: Michael Ellerman <[email protected]> (powerpc) Signed-off-by: Jiri Kosina <[email protected]>
1 parent 0ef7687 commit 43347d5

File tree

8 files changed

+102
-9
lines changed

8 files changed

+102
-9
lines changed

Documentation/ABI/testing/sysfs-kernel-livepatch

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,18 @@ Description:
3333
An attribute which indicates whether the patch is currently in
3434
transition.
3535

36+
What: /sys/kernel/livepatch/<patch>/signal
37+
Date: Nov 2017
38+
KernelVersion: 4.15.0
39+
40+
Description:
41+
A writable attribute that allows administrator to affect the
42+
course of an existing transition. Writing 1 sends a fake
43+
signal to all remaining blocking tasks. The fake signal
44+
means that no proper signal is delivered (there is no data in
45+
signal pending structures). Tasks are interrupted or woken up,
46+
and forced to change their patched state.
47+
3648
What: /sys/kernel/livepatch/<patch>/<object>
3749
Date: Nov 2014
3850
KernelVersion: 3.19.0

Documentation/livepatch/livepatch.txt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,12 @@ If a patch is in transition, this file shows 0 to indicate the task is
176176
unpatched and 1 to indicate it's patched. Otherwise, if no patch is in
177177
transition, it shows -1. Any tasks which are blocking the transition
178178
can be signaled with SIGSTOP and SIGCONT to force them to change their
179-
patched state.
180-
179+
patched state. This may be harmful to the system though.
180+
/sys/kernel/livepatch/<patch>/signal attribute provides a better alternative.
181+
Writing 1 to the attribute sends a fake signal to all remaining blocking
182+
tasks. No proper signal is actually delivered (there is no data in signal
183+
pending structures). Tasks are interrupted or woken up, and forced to change
184+
their patched state.
181185

182186
3.1 Adding consistency model support to new architectures
183187
---------------------------------------------------------
@@ -435,6 +439,9 @@ Information about the registered patches can be found under
435439
/sys/kernel/livepatch. The patches could be enabled and disabled
436440
by writing there.
437441

442+
/sys/kernel/livepatch/<patch>/signal attribute allows administrator to affect a
443+
patching operation.
444+
438445
See Documentation/ABI/testing/sysfs-kernel-livepatch for more details.
439446

440447

arch/powerpc/kernel/signal.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags)
153153
if (thread_info_flags & _TIF_UPROBE)
154154
uprobe_notify_resume(regs);
155155

156+
if (thread_info_flags & _TIF_PATCH_PENDING)
157+
klp_update_patch_state(current);
158+
156159
if (thread_info_flags & _TIF_SIGPENDING) {
157160
BUG_ON(regs != current->thread.regs);
158161
do_signal(current);
@@ -163,9 +166,6 @@ void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags)
163166
tracehook_notify_resume(regs);
164167
}
165168

166-
if (thread_info_flags & _TIF_PATCH_PENDING)
167-
klp_update_patch_state(current);
168-
169169
user_enter();
170170
}
171171

arch/x86/entry/common.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ static void exit_to_usermode_loop(struct pt_regs *regs, u32 cached_flags)
153153
if (cached_flags & _TIF_UPROBE)
154154
uprobe_notify_resume(regs);
155155

156+
if (cached_flags & _TIF_PATCH_PENDING)
157+
klp_update_patch_state(current);
158+
156159
/* deal with pending signal delivery */
157160
if (cached_flags & _TIF_SIGPENDING)
158161
do_signal(regs);
@@ -165,9 +168,6 @@ static void exit_to_usermode_loop(struct pt_regs *regs, u32 cached_flags)
165168
if (cached_flags & _TIF_USER_RETURN_NOTIFY)
166169
fire_user_return_notifiers();
167170

168-
if (cached_flags & _TIF_PATCH_PENDING)
169-
klp_update_patch_state(current);
170-
171171
/* Disable IRQs and retry */
172172
local_irq_disable();
173173

kernel/livepatch/core.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,7 @@ EXPORT_SYMBOL_GPL(klp_enable_patch);
454454
* /sys/kernel/livepatch/<patch>
455455
* /sys/kernel/livepatch/<patch>/enabled
456456
* /sys/kernel/livepatch/<patch>/transition
457+
* /sys/kernel/livepatch/<patch>/signal
457458
* /sys/kernel/livepatch/<patch>/<object>
458459
* /sys/kernel/livepatch/<patch>/<object>/<function,sympos>
459460
*/
@@ -528,11 +529,40 @@ static ssize_t transition_show(struct kobject *kobj,
528529
patch == klp_transition_patch);
529530
}
530531

532+
static ssize_t signal_store(struct kobject *kobj, struct kobj_attribute *attr,
533+
const char *buf, size_t count)
534+
{
535+
struct klp_patch *patch;
536+
int ret;
537+
bool val;
538+
539+
patch = container_of(kobj, struct klp_patch, kobj);
540+
541+
/*
542+
* klp_mutex lock is not grabbed here intentionally. It is not really
543+
* needed. The race window is harmless and grabbing the lock would only
544+
* hold the action back.
545+
*/
546+
if (patch != klp_transition_patch)
547+
return -EINVAL;
548+
549+
ret = kstrtobool(buf, &val);
550+
if (ret)
551+
return ret;
552+
553+
if (val)
554+
klp_send_signals();
555+
556+
return count;
557+
}
558+
531559
static struct kobj_attribute enabled_kobj_attr = __ATTR_RW(enabled);
532560
static struct kobj_attribute transition_kobj_attr = __ATTR_RO(transition);
561+
static struct kobj_attribute signal_kobj_attr = __ATTR_WO(signal);
533562
static struct attribute *klp_patch_attrs[] = {
534563
&enabled_kobj_attr.attr,
535564
&transition_kobj_attr.attr,
565+
&signal_kobj_attr.attr,
536566
NULL
537567
};
538568

kernel/livepatch/transition.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,3 +608,44 @@ void klp_copy_process(struct task_struct *child)
608608

609609
/* TIF_PATCH_PENDING gets copied in setup_thread_stack() */
610610
}
611+
612+
/*
613+
* Sends a fake signal to all non-kthread tasks with TIF_PATCH_PENDING set.
614+
* Kthreads with TIF_PATCH_PENDING set are woken up. Only admin can request this
615+
* action currently.
616+
*/
617+
void klp_send_signals(void)
618+
{
619+
struct task_struct *g, *task;
620+
621+
pr_notice("signaling remaining tasks\n");
622+
623+
read_lock(&tasklist_lock);
624+
for_each_process_thread(g, task) {
625+
if (!klp_patch_pending(task))
626+
continue;
627+
628+
/*
629+
* There is a small race here. We could see TIF_PATCH_PENDING
630+
* set and decide to wake up a kthread or send a fake signal.
631+
* Meanwhile the task could migrate itself and the action
632+
* would be meaningless. It is not serious though.
633+
*/
634+
if (task->flags & PF_KTHREAD) {
635+
/*
636+
* Wake up a kthread which sleeps interruptedly and
637+
* still has not been migrated.
638+
*/
639+
wake_up_state(task, TASK_INTERRUPTIBLE);
640+
} else {
641+
/*
642+
* Send fake signal to all non-kthread tasks which are
643+
* still not migrated.
644+
*/
645+
spin_lock_irq(&task->sighand->siglock);
646+
signal_wake_up(task, 0);
647+
spin_unlock_irq(&task->sighand->siglock);
648+
}
649+
}
650+
read_unlock(&tasklist_lock);
651+
}

kernel/livepatch/transition.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ void klp_cancel_transition(void);
1111
void klp_start_transition(void);
1212
void klp_try_complete_transition(void);
1313
void klp_reverse_transition(void);
14+
void klp_send_signals(void);
1415

1516
#endif /* _LIVEPATCH_TRANSITION_H */

kernel/signal.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include <linux/cn_proc.h>
4141
#include <linux/compiler.h>
4242
#include <linux/posix-timers.h>
43+
#include <linux/livepatch.h>
4344

4445
#define CREATE_TRACE_POINTS
4546
#include <trace/events/signal.h>
@@ -163,7 +164,8 @@ void recalc_sigpending_and_wake(struct task_struct *t)
163164

164165
void recalc_sigpending(void)
165166
{
166-
if (!recalc_sigpending_tsk(current) && !freezing(current))
167+
if (!recalc_sigpending_tsk(current) && !freezing(current) &&
168+
!klp_patch_pending(current))
167169
clear_thread_flag(TIF_SIGPENDING);
168170

169171
}

0 commit comments

Comments
 (0)