xref: /linux/kernel/livepatch/patch.c (revision c349cdcaba589fb49cf105093ebc695eb8b9ff08)
1*c349cdcaSJosh Poimboeuf /*
2*c349cdcaSJosh Poimboeuf  * patch.c - livepatch patching functions
3*c349cdcaSJosh Poimboeuf  *
4*c349cdcaSJosh Poimboeuf  * Copyright (C) 2014 Seth Jennings <sjenning@redhat.com>
5*c349cdcaSJosh Poimboeuf  * Copyright (C) 2014 SUSE
6*c349cdcaSJosh Poimboeuf  * Copyright (C) 2015 Josh Poimboeuf <jpoimboe@redhat.com>
7*c349cdcaSJosh Poimboeuf  *
8*c349cdcaSJosh Poimboeuf  * This program is free software; you can redistribute it and/or
9*c349cdcaSJosh Poimboeuf  * modify it under the terms of the GNU General Public License
10*c349cdcaSJosh Poimboeuf  * as published by the Free Software Foundation; either version 2
11*c349cdcaSJosh Poimboeuf  * of the License, or (at your option) any later version.
12*c349cdcaSJosh Poimboeuf  *
13*c349cdcaSJosh Poimboeuf  * This program is distributed in the hope that it will be useful,
14*c349cdcaSJosh Poimboeuf  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15*c349cdcaSJosh Poimboeuf  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16*c349cdcaSJosh Poimboeuf  * GNU General Public License for more details.
17*c349cdcaSJosh Poimboeuf  *
18*c349cdcaSJosh Poimboeuf  * You should have received a copy of the GNU General Public License
19*c349cdcaSJosh Poimboeuf  * along with this program; if not, see <http://www.gnu.org/licenses/>.
20*c349cdcaSJosh Poimboeuf  */
21*c349cdcaSJosh Poimboeuf 
22*c349cdcaSJosh Poimboeuf #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
23*c349cdcaSJosh Poimboeuf 
24*c349cdcaSJosh Poimboeuf #include <linux/livepatch.h>
25*c349cdcaSJosh Poimboeuf #include <linux/list.h>
26*c349cdcaSJosh Poimboeuf #include <linux/ftrace.h>
27*c349cdcaSJosh Poimboeuf #include <linux/rculist.h>
28*c349cdcaSJosh Poimboeuf #include <linux/slab.h>
29*c349cdcaSJosh Poimboeuf #include <linux/bug.h>
30*c349cdcaSJosh Poimboeuf #include <linux/printk.h>
31*c349cdcaSJosh Poimboeuf #include "patch.h"
32*c349cdcaSJosh Poimboeuf 
33*c349cdcaSJosh Poimboeuf static LIST_HEAD(klp_ops);
34*c349cdcaSJosh Poimboeuf 
35*c349cdcaSJosh Poimboeuf struct klp_ops *klp_find_ops(unsigned long old_addr)
36*c349cdcaSJosh Poimboeuf {
37*c349cdcaSJosh Poimboeuf 	struct klp_ops *ops;
38*c349cdcaSJosh Poimboeuf 	struct klp_func *func;
39*c349cdcaSJosh Poimboeuf 
40*c349cdcaSJosh Poimboeuf 	list_for_each_entry(ops, &klp_ops, node) {
41*c349cdcaSJosh Poimboeuf 		func = list_first_entry(&ops->func_stack, struct klp_func,
42*c349cdcaSJosh Poimboeuf 					stack_node);
43*c349cdcaSJosh Poimboeuf 		if (func->old_addr == old_addr)
44*c349cdcaSJosh Poimboeuf 			return ops;
45*c349cdcaSJosh Poimboeuf 	}
46*c349cdcaSJosh Poimboeuf 
47*c349cdcaSJosh Poimboeuf 	return NULL;
48*c349cdcaSJosh Poimboeuf }
49*c349cdcaSJosh Poimboeuf 
50*c349cdcaSJosh Poimboeuf static void notrace klp_ftrace_handler(unsigned long ip,
51*c349cdcaSJosh Poimboeuf 				       unsigned long parent_ip,
52*c349cdcaSJosh Poimboeuf 				       struct ftrace_ops *fops,
53*c349cdcaSJosh Poimboeuf 				       struct pt_regs *regs)
54*c349cdcaSJosh Poimboeuf {
55*c349cdcaSJosh Poimboeuf 	struct klp_ops *ops;
56*c349cdcaSJosh Poimboeuf 	struct klp_func *func;
57*c349cdcaSJosh Poimboeuf 
58*c349cdcaSJosh Poimboeuf 	ops = container_of(fops, struct klp_ops, fops);
59*c349cdcaSJosh Poimboeuf 
60*c349cdcaSJosh Poimboeuf 	rcu_read_lock();
61*c349cdcaSJosh Poimboeuf 	func = list_first_or_null_rcu(&ops->func_stack, struct klp_func,
62*c349cdcaSJosh Poimboeuf 				      stack_node);
63*c349cdcaSJosh Poimboeuf 	if (WARN_ON_ONCE(!func))
64*c349cdcaSJosh Poimboeuf 		goto unlock;
65*c349cdcaSJosh Poimboeuf 
66*c349cdcaSJosh Poimboeuf 	klp_arch_set_pc(regs, (unsigned long)func->new_func);
67*c349cdcaSJosh Poimboeuf unlock:
68*c349cdcaSJosh Poimboeuf 	rcu_read_unlock();
69*c349cdcaSJosh Poimboeuf }
70*c349cdcaSJosh Poimboeuf 
71*c349cdcaSJosh Poimboeuf /*
72*c349cdcaSJosh Poimboeuf  * Convert a function address into the appropriate ftrace location.
73*c349cdcaSJosh Poimboeuf  *
74*c349cdcaSJosh Poimboeuf  * Usually this is just the address of the function, but on some architectures
75*c349cdcaSJosh Poimboeuf  * it's more complicated so allow them to provide a custom behaviour.
76*c349cdcaSJosh Poimboeuf  */
77*c349cdcaSJosh Poimboeuf #ifndef klp_get_ftrace_location
78*c349cdcaSJosh Poimboeuf static unsigned long klp_get_ftrace_location(unsigned long faddr)
79*c349cdcaSJosh Poimboeuf {
80*c349cdcaSJosh Poimboeuf 	return faddr;
81*c349cdcaSJosh Poimboeuf }
82*c349cdcaSJosh Poimboeuf #endif
83*c349cdcaSJosh Poimboeuf 
84*c349cdcaSJosh Poimboeuf static void klp_unpatch_func(struct klp_func *func)
85*c349cdcaSJosh Poimboeuf {
86*c349cdcaSJosh Poimboeuf 	struct klp_ops *ops;
87*c349cdcaSJosh Poimboeuf 
88*c349cdcaSJosh Poimboeuf 	if (WARN_ON(!func->patched))
89*c349cdcaSJosh Poimboeuf 		return;
90*c349cdcaSJosh Poimboeuf 	if (WARN_ON(!func->old_addr))
91*c349cdcaSJosh Poimboeuf 		return;
92*c349cdcaSJosh Poimboeuf 
93*c349cdcaSJosh Poimboeuf 	ops = klp_find_ops(func->old_addr);
94*c349cdcaSJosh Poimboeuf 	if (WARN_ON(!ops))
95*c349cdcaSJosh Poimboeuf 		return;
96*c349cdcaSJosh Poimboeuf 
97*c349cdcaSJosh Poimboeuf 	if (list_is_singular(&ops->func_stack)) {
98*c349cdcaSJosh Poimboeuf 		unsigned long ftrace_loc;
99*c349cdcaSJosh Poimboeuf 
100*c349cdcaSJosh Poimboeuf 		ftrace_loc = klp_get_ftrace_location(func->old_addr);
101*c349cdcaSJosh Poimboeuf 		if (WARN_ON(!ftrace_loc))
102*c349cdcaSJosh Poimboeuf 			return;
103*c349cdcaSJosh Poimboeuf 
104*c349cdcaSJosh Poimboeuf 		WARN_ON(unregister_ftrace_function(&ops->fops));
105*c349cdcaSJosh Poimboeuf 		WARN_ON(ftrace_set_filter_ip(&ops->fops, ftrace_loc, 1, 0));
106*c349cdcaSJosh Poimboeuf 
107*c349cdcaSJosh Poimboeuf 		list_del_rcu(&func->stack_node);
108*c349cdcaSJosh Poimboeuf 		list_del(&ops->node);
109*c349cdcaSJosh Poimboeuf 		kfree(ops);
110*c349cdcaSJosh Poimboeuf 	} else {
111*c349cdcaSJosh Poimboeuf 		list_del_rcu(&func->stack_node);
112*c349cdcaSJosh Poimboeuf 	}
113*c349cdcaSJosh Poimboeuf 
114*c349cdcaSJosh Poimboeuf 	func->patched = false;
115*c349cdcaSJosh Poimboeuf }
116*c349cdcaSJosh Poimboeuf 
117*c349cdcaSJosh Poimboeuf static int klp_patch_func(struct klp_func *func)
118*c349cdcaSJosh Poimboeuf {
119*c349cdcaSJosh Poimboeuf 	struct klp_ops *ops;
120*c349cdcaSJosh Poimboeuf 	int ret;
121*c349cdcaSJosh Poimboeuf 
122*c349cdcaSJosh Poimboeuf 	if (WARN_ON(!func->old_addr))
123*c349cdcaSJosh Poimboeuf 		return -EINVAL;
124*c349cdcaSJosh Poimboeuf 
125*c349cdcaSJosh Poimboeuf 	if (WARN_ON(func->patched))
126*c349cdcaSJosh Poimboeuf 		return -EINVAL;
127*c349cdcaSJosh Poimboeuf 
128*c349cdcaSJosh Poimboeuf 	ops = klp_find_ops(func->old_addr);
129*c349cdcaSJosh Poimboeuf 	if (!ops) {
130*c349cdcaSJosh Poimboeuf 		unsigned long ftrace_loc;
131*c349cdcaSJosh Poimboeuf 
132*c349cdcaSJosh Poimboeuf 		ftrace_loc = klp_get_ftrace_location(func->old_addr);
133*c349cdcaSJosh Poimboeuf 		if (!ftrace_loc) {
134*c349cdcaSJosh Poimboeuf 			pr_err("failed to find location for function '%s'\n",
135*c349cdcaSJosh Poimboeuf 				func->old_name);
136*c349cdcaSJosh Poimboeuf 			return -EINVAL;
137*c349cdcaSJosh Poimboeuf 		}
138*c349cdcaSJosh Poimboeuf 
139*c349cdcaSJosh Poimboeuf 		ops = kzalloc(sizeof(*ops), GFP_KERNEL);
140*c349cdcaSJosh Poimboeuf 		if (!ops)
141*c349cdcaSJosh Poimboeuf 			return -ENOMEM;
142*c349cdcaSJosh Poimboeuf 
143*c349cdcaSJosh Poimboeuf 		ops->fops.func = klp_ftrace_handler;
144*c349cdcaSJosh Poimboeuf 		ops->fops.flags = FTRACE_OPS_FL_SAVE_REGS |
145*c349cdcaSJosh Poimboeuf 				  FTRACE_OPS_FL_DYNAMIC |
146*c349cdcaSJosh Poimboeuf 				  FTRACE_OPS_FL_IPMODIFY;
147*c349cdcaSJosh Poimboeuf 
148*c349cdcaSJosh Poimboeuf 		list_add(&ops->node, &klp_ops);
149*c349cdcaSJosh Poimboeuf 
150*c349cdcaSJosh Poimboeuf 		INIT_LIST_HEAD(&ops->func_stack);
151*c349cdcaSJosh Poimboeuf 		list_add_rcu(&func->stack_node, &ops->func_stack);
152*c349cdcaSJosh Poimboeuf 
153*c349cdcaSJosh Poimboeuf 		ret = ftrace_set_filter_ip(&ops->fops, ftrace_loc, 0, 0);
154*c349cdcaSJosh Poimboeuf 		if (ret) {
155*c349cdcaSJosh Poimboeuf 			pr_err("failed to set ftrace filter for function '%s' (%d)\n",
156*c349cdcaSJosh Poimboeuf 			       func->old_name, ret);
157*c349cdcaSJosh Poimboeuf 			goto err;
158*c349cdcaSJosh Poimboeuf 		}
159*c349cdcaSJosh Poimboeuf 
160*c349cdcaSJosh Poimboeuf 		ret = register_ftrace_function(&ops->fops);
161*c349cdcaSJosh Poimboeuf 		if (ret) {
162*c349cdcaSJosh Poimboeuf 			pr_err("failed to register ftrace handler for function '%s' (%d)\n",
163*c349cdcaSJosh Poimboeuf 			       func->old_name, ret);
164*c349cdcaSJosh Poimboeuf 			ftrace_set_filter_ip(&ops->fops, ftrace_loc, 1, 0);
165*c349cdcaSJosh Poimboeuf 			goto err;
166*c349cdcaSJosh Poimboeuf 		}
167*c349cdcaSJosh Poimboeuf 
168*c349cdcaSJosh Poimboeuf 
169*c349cdcaSJosh Poimboeuf 	} else {
170*c349cdcaSJosh Poimboeuf 		list_add_rcu(&func->stack_node, &ops->func_stack);
171*c349cdcaSJosh Poimboeuf 	}
172*c349cdcaSJosh Poimboeuf 
173*c349cdcaSJosh Poimboeuf 	func->patched = true;
174*c349cdcaSJosh Poimboeuf 
175*c349cdcaSJosh Poimboeuf 	return 0;
176*c349cdcaSJosh Poimboeuf 
177*c349cdcaSJosh Poimboeuf err:
178*c349cdcaSJosh Poimboeuf 	list_del_rcu(&func->stack_node);
179*c349cdcaSJosh Poimboeuf 	list_del(&ops->node);
180*c349cdcaSJosh Poimboeuf 	kfree(ops);
181*c349cdcaSJosh Poimboeuf 	return ret;
182*c349cdcaSJosh Poimboeuf }
183*c349cdcaSJosh Poimboeuf 
184*c349cdcaSJosh Poimboeuf void klp_unpatch_object(struct klp_object *obj)
185*c349cdcaSJosh Poimboeuf {
186*c349cdcaSJosh Poimboeuf 	struct klp_func *func;
187*c349cdcaSJosh Poimboeuf 
188*c349cdcaSJosh Poimboeuf 	klp_for_each_func(obj, func)
189*c349cdcaSJosh Poimboeuf 		if (func->patched)
190*c349cdcaSJosh Poimboeuf 			klp_unpatch_func(func);
191*c349cdcaSJosh Poimboeuf 
192*c349cdcaSJosh Poimboeuf 	obj->patched = false;
193*c349cdcaSJosh Poimboeuf }
194*c349cdcaSJosh Poimboeuf 
195*c349cdcaSJosh Poimboeuf int klp_patch_object(struct klp_object *obj)
196*c349cdcaSJosh Poimboeuf {
197*c349cdcaSJosh Poimboeuf 	struct klp_func *func;
198*c349cdcaSJosh Poimboeuf 	int ret;
199*c349cdcaSJosh Poimboeuf 
200*c349cdcaSJosh Poimboeuf 	if (WARN_ON(obj->patched))
201*c349cdcaSJosh Poimboeuf 		return -EINVAL;
202*c349cdcaSJosh Poimboeuf 
203*c349cdcaSJosh Poimboeuf 	klp_for_each_func(obj, func) {
204*c349cdcaSJosh Poimboeuf 		ret = klp_patch_func(func);
205*c349cdcaSJosh Poimboeuf 		if (ret) {
206*c349cdcaSJosh Poimboeuf 			klp_unpatch_object(obj);
207*c349cdcaSJosh Poimboeuf 			return ret;
208*c349cdcaSJosh Poimboeuf 		}
209*c349cdcaSJosh Poimboeuf 	}
210*c349cdcaSJosh Poimboeuf 	obj->patched = true;
211*c349cdcaSJosh Poimboeuf 
212*c349cdcaSJosh Poimboeuf 	return 0;
213*c349cdcaSJosh Poimboeuf }
214