Add three exported API for livepatch modules: void *klp_shadow_attach(void *obj, char *var, gfp_t gfp, void *data); void klp_shadow_detach(void *obj, char *var); void *klp_shadow_get(void *obj, char *var); that implement "shadow" variables, which allow callers to associate new shadow fields to existing data structures. Signed-off-by: Joe Lawrence <joe.lawrence@xxxxxxxxxx> --- include/linux/livepatch.h | 4 ++ kernel/livepatch/Makefile | 2 +- kernel/livepatch/shadow.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 kernel/livepatch/shadow.c diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h index 194991ef9347..a0d068ecf7f8 100644 --- a/include/linux/livepatch.h +++ b/include/linux/livepatch.h @@ -164,6 +164,10 @@ static inline bool klp_have_reliable_stack(void) IS_ENABLED(CONFIG_HAVE_RELIABLE_STACKTRACE); } +void *klp_shadow_attach(void *obj, char *var, gfp_t gfp, void *data); +void klp_shadow_detach(void *obj, char *var); +void *klp_shadow_get(void *obj, char *var); + #else /* !CONFIG_LIVEPATCH */ static inline int klp_module_coming(struct module *mod) { return 0; } diff --git a/kernel/livepatch/Makefile b/kernel/livepatch/Makefile index 2b8bdb1925da..b36ceda6488e 100644 --- a/kernel/livepatch/Makefile +++ b/kernel/livepatch/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_LIVEPATCH) += livepatch.o -livepatch-objs := core.o patch.o transition.o +livepatch-objs := core.o patch.o shadow.o transition.o diff --git a/kernel/livepatch/shadow.c b/kernel/livepatch/shadow.c new file mode 100644 index 000000000000..72d5e567dff9 --- /dev/null +++ b/kernel/livepatch/shadow.c @@ -0,0 +1,115 @@ +/* + * shadow.c - Shadow Variables + * + * Copyright (C) 2014 Josh Poimboeuf <jpoimboe@xxxxxxxxxx> + * Copyright (C) 2014 Seth Jennings <sjenning@xxxxxxxxxx> + * Copyright (C) 2017 Joe Lawrence <joe.lawrence@xxxxxxxxxx> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/hashtable.h> +#include <linux/slab.h> +#include <linux/livepatch.h> + +static DEFINE_HASHTABLE(klp_shadow_hash, 12); +static DEFINE_SPINLOCK(klp_shadow_lock); + +struct klp_shadow { + struct hlist_node node; + struct rcu_head rcu_head; + void *obj; + char *var; + void *data; +}; + +void *klp_shadow_attach(void *obj, char *var, gfp_t gfp, void *data) +{ + unsigned long flags; + struct klp_shadow *shadow; + + shadow = kmalloc(sizeof(*shadow), gfp); + if (!shadow) + return NULL; + + shadow->obj = obj; + + shadow->var = kstrdup(var, gfp); + if (!shadow->var) { + kfree(shadow); + return NULL; + } + + shadow->data = data; + + spin_lock_irqsave(&klp_shadow_lock, flags); + hash_add_rcu(klp_shadow_hash, &shadow->node, (unsigned long)obj); + spin_unlock_irqrestore(&klp_shadow_lock, flags); + + return shadow->data; +} +EXPORT_SYMBOL_GPL(klp_shadow_attach); + +static void klp_shadow_rcu_free(struct rcu_head *head) +{ + struct klp_shadow *shadow; + + shadow = container_of(head, struct klp_shadow, rcu_head); + + kfree(shadow->var); + kfree(shadow); +} + +void klp_shadow_detach(void *obj, char *var) +{ + unsigned long flags; + struct klp_shadow *shadow; + + spin_lock_irqsave(&klp_shadow_lock, flags); + + hash_for_each_possible(klp_shadow_hash, shadow, node, + (unsigned long)obj) { + if (shadow->obj == obj && !strcmp(shadow->var, var)) { + hash_del_rcu(&shadow->node); + spin_unlock_irqrestore(&klp_shadow_lock, flags); + call_rcu(&shadow->rcu_head, klp_shadow_rcu_free); + return; + } + } + + spin_unlock_irqrestore(&klp_shadow_lock, flags); +} +EXPORT_SYMBOL_GPL(klp_shadow_detach); + +void *klp_shadow_get(void *obj, char *var) +{ + struct klp_shadow *shadow; + + rcu_read_lock(); + + hash_for_each_possible_rcu(klp_shadow_hash, shadow, node, + (unsigned long)obj) { + if (shadow->obj == obj && !strcmp(shadow->var, var)) { + rcu_read_unlock(); + return shadow->data; + } + } + + rcu_read_unlock(); + + return NULL; +} +EXPORT_SYMBOL_GPL(klp_shadow_get); -- 1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe live-patching" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html