diff options
Diffstat (limited to 'kernel/livepatch')
| -rw-r--r-- | kernel/livepatch/core.c | 30 | ||||
| -rw-r--r-- | kernel/livepatch/transition.c | 36 | ||||
| -rw-r--r-- | kernel/livepatch/transition.h | 1 |
3 files changed, 65 insertions, 2 deletions
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index 88766bd91803..1c3c9b27c916 100644 --- a/kernel/livepatch/core.c +++ b/kernel/livepatch/core.c | |||
| @@ -455,6 +455,7 @@ EXPORT_SYMBOL_GPL(klp_enable_patch); | |||
| 455 | * /sys/kernel/livepatch/<patch>/enabled | 455 | * /sys/kernel/livepatch/<patch>/enabled |
| 456 | * /sys/kernel/livepatch/<patch>/transition | 456 | * /sys/kernel/livepatch/<patch>/transition |
| 457 | * /sys/kernel/livepatch/<patch>/signal | 457 | * /sys/kernel/livepatch/<patch>/signal |
| 458 | * /sys/kernel/livepatch/<patch>/force | ||
| 458 | * /sys/kernel/livepatch/<patch>/<object> | 459 | * /sys/kernel/livepatch/<patch>/<object> |
| 459 | * /sys/kernel/livepatch/<patch>/<object>/<function,sympos> | 460 | * /sys/kernel/livepatch/<patch>/<object>/<function,sympos> |
| 460 | */ | 461 | */ |
| @@ -556,13 +557,42 @@ static ssize_t signal_store(struct kobject *kobj, struct kobj_attribute *attr, | |||
| 556 | return count; | 557 | return count; |
| 557 | } | 558 | } |
| 558 | 559 | ||
| 560 | static ssize_t force_store(struct kobject *kobj, struct kobj_attribute *attr, | ||
| 561 | const char *buf, size_t count) | ||
| 562 | { | ||
| 563 | struct klp_patch *patch; | ||
| 564 | int ret; | ||
| 565 | bool val; | ||
| 566 | |||
| 567 | patch = container_of(kobj, struct klp_patch, kobj); | ||
| 568 | |||
| 569 | /* | ||
| 570 | * klp_mutex lock is not grabbed here intentionally. It is not really | ||
| 571 | * needed. The race window is harmless and grabbing the lock would only | ||
| 572 | * hold the action back. | ||
| 573 | */ | ||
| 574 | if (patch != klp_transition_patch) | ||
| 575 | return -EINVAL; | ||
| 576 | |||
| 577 | ret = kstrtobool(buf, &val); | ||
| 578 | if (ret) | ||
| 579 | return ret; | ||
| 580 | |||
| 581 | if (val) | ||
| 582 | klp_force_transition(); | ||
| 583 | |||
| 584 | return count; | ||
| 585 | } | ||
| 586 | |||
| 559 | static struct kobj_attribute enabled_kobj_attr = __ATTR_RW(enabled); | 587 | static struct kobj_attribute enabled_kobj_attr = __ATTR_RW(enabled); |
| 560 | static struct kobj_attribute transition_kobj_attr = __ATTR_RO(transition); | 588 | static struct kobj_attribute transition_kobj_attr = __ATTR_RO(transition); |
| 561 | static struct kobj_attribute signal_kobj_attr = __ATTR_WO(signal); | 589 | static struct kobj_attribute signal_kobj_attr = __ATTR_WO(signal); |
| 590 | static struct kobj_attribute force_kobj_attr = __ATTR_WO(force); | ||
| 562 | static struct attribute *klp_patch_attrs[] = { | 591 | static struct attribute *klp_patch_attrs[] = { |
| 563 | &enabled_kobj_attr.attr, | 592 | &enabled_kobj_attr.attr, |
| 564 | &transition_kobj_attr.attr, | 593 | &transition_kobj_attr.attr, |
| 565 | &signal_kobj_attr.attr, | 594 | &signal_kobj_attr.attr, |
| 595 | &force_kobj_attr.attr, | ||
| 566 | NULL | 596 | NULL |
| 567 | }; | 597 | }; |
| 568 | 598 | ||
diff --git a/kernel/livepatch/transition.c b/kernel/livepatch/transition.c index edcfcb8ebb2d..be5bfa533ee8 100644 --- a/kernel/livepatch/transition.c +++ b/kernel/livepatch/transition.c | |||
| @@ -33,6 +33,8 @@ struct klp_patch *klp_transition_patch; | |||
| 33 | 33 | ||
| 34 | static int klp_target_state = KLP_UNDEFINED; | 34 | static int klp_target_state = KLP_UNDEFINED; |
| 35 | 35 | ||
| 36 | static bool klp_forced = false; | ||
| 37 | |||
| 36 | /* | 38 | /* |
| 37 | * This work can be performed periodically to finish patching or unpatching any | 39 | * This work can be performed periodically to finish patching or unpatching any |
| 38 | * "straggler" tasks which failed to transition in the first attempt. | 40 | * "straggler" tasks which failed to transition in the first attempt. |
| @@ -146,9 +148,12 @@ done: | |||
| 146 | /* | 148 | /* |
| 147 | * See complementary comment in __klp_enable_patch() for why we | 149 | * See complementary comment in __klp_enable_patch() for why we |
| 148 | * keep the module reference for immediate patches. | 150 | * keep the module reference for immediate patches. |
| 151 | * | ||
| 152 | * klp_forced or immediate_func set implies unbounded increase of | ||
| 153 | * module's ref count if the module is disabled/enabled in a loop. | ||
| 149 | */ | 154 | */ |
| 150 | if (!klp_transition_patch->immediate && !immediate_func && | 155 | if (!klp_forced && !klp_transition_patch->immediate && |
| 151 | klp_target_state == KLP_UNPATCHED) { | 156 | !immediate_func && klp_target_state == KLP_UNPATCHED) { |
| 152 | module_put(klp_transition_patch->mod); | 157 | module_put(klp_transition_patch->mod); |
| 153 | } | 158 | } |
| 154 | 159 | ||
| @@ -649,3 +654,30 @@ void klp_send_signals(void) | |||
| 649 | } | 654 | } |
| 650 | read_unlock(&tasklist_lock); | 655 | read_unlock(&tasklist_lock); |
| 651 | } | 656 | } |
| 657 | |||
| 658 | /* | ||
| 659 | * Drop TIF_PATCH_PENDING of all tasks on admin's request. This forces an | ||
| 660 | * existing transition to finish. | ||
| 661 | * | ||
| 662 | * NOTE: klp_update_patch_state(task) requires the task to be inactive or | ||
| 663 | * 'current'. This is not the case here and the consistency model could be | ||
| 664 | * broken. Administrator, who is the only one to execute the | ||
| 665 | * klp_force_transitions(), has to be aware of this. | ||
| 666 | */ | ||
| 667 | void klp_force_transition(void) | ||
| 668 | { | ||
| 669 | struct task_struct *g, *task; | ||
| 670 | unsigned int cpu; | ||
| 671 | |||
| 672 | pr_warn("forcing remaining tasks to the patched state\n"); | ||
| 673 | |||
| 674 | read_lock(&tasklist_lock); | ||
| 675 | for_each_process_thread(g, task) | ||
| 676 | klp_update_patch_state(task); | ||
| 677 | read_unlock(&tasklist_lock); | ||
| 678 | |||
| 679 | for_each_possible_cpu(cpu) | ||
| 680 | klp_update_patch_state(idle_task(cpu)); | ||
| 681 | |||
| 682 | klp_forced = true; | ||
| 683 | } | ||
diff --git a/kernel/livepatch/transition.h b/kernel/livepatch/transition.h index 40522795a5f6..f9d0bc016067 100644 --- a/kernel/livepatch/transition.h +++ b/kernel/livepatch/transition.h | |||
| @@ -12,5 +12,6 @@ void klp_start_transition(void); | |||
| 12 | void klp_try_complete_transition(void); | 12 | void klp_try_complete_transition(void); |
| 13 | void klp_reverse_transition(void); | 13 | void klp_reverse_transition(void); |
| 14 | void klp_send_signals(void); | 14 | void klp_send_signals(void); |
| 15 | void klp_force_transition(void); | ||
| 15 | 16 | ||
| 16 | #endif /* _LIVEPATCH_TRANSITION_H */ | 17 | #endif /* _LIVEPATCH_TRANSITION_H */ |
