aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/livepatch
diff options
context:
space:
mode:
authorJiri Kosina <jkosina@suse.cz>2017-05-01 15:49:28 -0400
committerJiri Kosina <jkosina@suse.cz>2017-05-01 15:49:28 -0400
commita0841609f658c77f066af9c61a2e13143564fcb4 (patch)
tree0f0df468b6f852501cd4ed1570701e695b9f5d56 /kernel/livepatch
parent77f8f39a2e463eca89a19b916189d0e4e38f75d8 (diff)
parente679af627fe875a51d40b9a2b17f08fbde36e0e2 (diff)
Merge branches 'for-4.12/upstream' and 'for-4.12/klp-hybrid-consistency-model' into for-linus
Diffstat (limited to 'kernel/livepatch')
-rw-r--r--kernel/livepatch/Makefile2
-rw-r--r--kernel/livepatch/core.c437
-rw-r--r--kernel/livepatch/core.h6
-rw-r--r--kernel/livepatch/patch.c272
-rw-r--r--kernel/livepatch/patch.h33
-rw-r--r--kernel/livepatch/transition.c553
-rw-r--r--kernel/livepatch/transition.h14
7 files changed, 1043 insertions, 274 deletions
diff --git a/kernel/livepatch/Makefile b/kernel/livepatch/Makefile
index e8780c0901d9..2b8bdb1925da 100644
--- a/kernel/livepatch/Makefile
+++ b/kernel/livepatch/Makefile
@@ -1,3 +1,3 @@
1obj-$(CONFIG_LIVEPATCH) += livepatch.o 1obj-$(CONFIG_LIVEPATCH) += livepatch.o
2 2
3livepatch-objs := core.o 3livepatch-objs := core.o patch.o transition.o
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index 8739e9e0bdb8..b9628e43c78f 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -24,61 +24,31 @@
24#include <linux/kernel.h> 24#include <linux/kernel.h>
25#include <linux/mutex.h> 25#include <linux/mutex.h>
26#include <linux/slab.h> 26#include <linux/slab.h>
27#include <linux/ftrace.h>
28#include <linux/list.h> 27#include <linux/list.h>
29#include <linux/kallsyms.h> 28#include <linux/kallsyms.h>
30#include <linux/livepatch.h> 29#include <linux/livepatch.h>
31#include <linux/elf.h> 30#include <linux/elf.h>
32#include <linux/moduleloader.h> 31#include <linux/moduleloader.h>
32#include <linux/completion.h>
33#include <asm/cacheflush.h> 33#include <asm/cacheflush.h>
34 34#include "core.h"
35/** 35#include "patch.h"
36 * struct klp_ops - structure for tracking registered ftrace ops structs 36#include "transition.h"
37 *
38 * A single ftrace_ops is shared between all enabled replacement functions
39 * (klp_func structs) which have the same old_addr. This allows the switch
40 * between function versions to happen instantaneously by updating the klp_ops
41 * struct's func_stack list. The winner is the klp_func at the top of the
42 * func_stack (front of the list).
43 *
44 * @node: node for the global klp_ops list
45 * @func_stack: list head for the stack of klp_func's (active func is on top)
46 * @fops: registered ftrace ops struct
47 */
48struct klp_ops {
49 struct list_head node;
50 struct list_head func_stack;
51 struct ftrace_ops fops;
52};
53 37
54/* 38/*
55 * The klp_mutex protects the global lists and state transitions of any 39 * klp_mutex is a coarse lock which serializes access to klp data. All
56 * structure reachable from them. References to any structure must be obtained 40 * accesses to klp-related variables and structures must have mutex protection,
57 * under mutex protection (except in klp_ftrace_handler(), which uses RCU to 41 * except within the following functions which carefully avoid the need for it:
58 * ensure it gets consistent data). 42 *
43 * - klp_ftrace_handler()
44 * - klp_update_patch_state()
59 */ 45 */
60static DEFINE_MUTEX(klp_mutex); 46DEFINE_MUTEX(klp_mutex);
61 47
62static LIST_HEAD(klp_patches); 48static LIST_HEAD(klp_patches);
63static LIST_HEAD(klp_ops);
64 49
65static struct kobject *klp_root_kobj; 50static struct kobject *klp_root_kobj;
66 51
67static struct klp_ops *klp_find_ops(unsigned long old_addr)
68{
69 struct klp_ops *ops;
70 struct klp_func *func;
71
72 list_for_each_entry(ops, &klp_ops, node) {
73 func = list_first_entry(&ops->func_stack, struct klp_func,
74 stack_node);
75 if (func->old_addr == old_addr)
76 return ops;
77 }
78
79 return NULL;
80}
81
82static bool klp_is_module(struct klp_object *obj) 52static bool klp_is_module(struct klp_object *obj)
83{ 53{
84 return obj->name; 54 return obj->name;
@@ -117,7 +87,6 @@ static void klp_find_object_module(struct klp_object *obj)
117 mutex_unlock(&module_mutex); 87 mutex_unlock(&module_mutex);
118} 88}
119 89
120/* klp_mutex must be held by caller */
121static bool klp_is_patch_registered(struct klp_patch *patch) 90static bool klp_is_patch_registered(struct klp_patch *patch)
122{ 91{
123 struct klp_patch *mypatch; 92 struct klp_patch *mypatch;
@@ -314,191 +283,30 @@ static int klp_write_object_relocations(struct module *pmod,
314 return ret; 283 return ret;
315} 284}
316 285
317static void notrace klp_ftrace_handler(unsigned long ip,
318 unsigned long parent_ip,
319 struct ftrace_ops *fops,
320 struct pt_regs *regs)
321{
322 struct klp_ops *ops;
323 struct klp_func *func;
324
325 ops = container_of(fops, struct klp_ops, fops);
326
327 rcu_read_lock();
328 func = list_first_or_null_rcu(&ops->func_stack, struct klp_func,
329 stack_node);
330 if (WARN_ON_ONCE(!func))
331 goto unlock;
332
333 klp_arch_set_pc(regs, (unsigned long)func->new_func);
334unlock:
335 rcu_read_unlock();
336}
337
338/*
339 * Convert a function address into the appropriate ftrace location.
340 *
341 * Usually this is just the address of the function, but on some architectures
342 * it's more complicated so allow them to provide a custom behaviour.
343 */
344#ifndef klp_get_ftrace_location
345static unsigned long klp_get_ftrace_location(unsigned long faddr)
346{
347 return faddr;
348}
349#endif
350
351static void klp_disable_func(struct klp_func *func)
352{
353 struct klp_ops *ops;
354
355 if (WARN_ON(func->state != KLP_ENABLED))
356 return;
357 if (WARN_ON(!func->old_addr))
358 return;
359
360 ops = klp_find_ops(func->old_addr);
361 if (WARN_ON(!ops))
362 return;
363
364 if (list_is_singular(&ops->func_stack)) {
365 unsigned long ftrace_loc;
366
367 ftrace_loc = klp_get_ftrace_location(func->old_addr);
368 if (WARN_ON(!ftrace_loc))
369 return;
370
371 WARN_ON(unregister_ftrace_function(&ops->fops));
372 WARN_ON(ftrace_set_filter_ip(&ops->fops, ftrace_loc, 1, 0));
373
374 list_del_rcu(&func->stack_node);
375 list_del(&ops->node);
376 kfree(ops);
377 } else {
378 list_del_rcu(&func->stack_node);
379 }
380
381 func->state = KLP_DISABLED;
382}
383
384static int klp_enable_func(struct klp_func *func)
385{
386 struct klp_ops *ops;
387 int ret;
388
389 if (WARN_ON(!func->old_addr))
390 return -EINVAL;
391
392 if (WARN_ON(func->state != KLP_DISABLED))
393 return -EINVAL;
394
395 ops = klp_find_ops(func->old_addr);
396 if (!ops) {
397 unsigned long ftrace_loc;
398
399 ftrace_loc = klp_get_ftrace_location(func->old_addr);
400 if (!ftrace_loc) {
401 pr_err("failed to find location for function '%s'\n",
402 func->old_name);
403 return -EINVAL;
404 }
405
406 ops = kzalloc(sizeof(*ops), GFP_KERNEL);
407 if (!ops)
408 return -ENOMEM;
409
410 ops->fops.func = klp_ftrace_handler;
411 ops->fops.flags = FTRACE_OPS_FL_SAVE_REGS |
412 FTRACE_OPS_FL_DYNAMIC |
413 FTRACE_OPS_FL_IPMODIFY;
414
415 list_add(&ops->node, &klp_ops);
416