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