// SPDX-License-Identifier: GPL-2.0-or-later/* * core.c - Kernel Live Patching Core * * Copyright (C) 2014 Seth Jennings <sjenning@redhat.com> * Copyright (C) 2014 SUSE */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h>#include <linux/kernel.h>#include <linux/mutex.h>#include <linux/slab.h>#include <linux/list.h>#include <linux/kallsyms.h>#include <linux/livepatch.h>#include <linux/elf.h>#include <linux/moduleloader.h>#include <linux/completion.h>#include <linux/memory.h>#include <linux/rcupdate.h>#include <asm/cacheflush.h>#include "core.h"#include "patch.h"#include "state.h"#include "transition.h" /* * klp_mutex is a coarse lock which serializes access to klp data. All * accesses to klp-related variables and structures must have mutex protection, * except within the following functions which carefully avoid the need for it: * * - klp_ftrace_handler() * - klp_update_patch_state() * - __klp_sched_try_switch() */DEFINE_MUTEX(klp_mutex); /* * Actively used patches: enabled or in transition. Note that replaced * or disabled patches are not listed even though the related kernel * module still can be loaded. */LIST_HEAD(klp_patches); static struct kobject *klp_root_kobj; static bool klp_is_module(struct klp_object *obj){ return obj->name;} /* sets obj->mod if object is not vmlinux and module is found */static void klp_find_object_module(struct klp_object *obj){ struct module *mod; if (!klp_is_module(obj)) return; rcu_read_lock_sched(); /* * We do not want to block removal of patched modules and therefore * we do not take a reference here. The patches are removed by * klp_module_going() instead. */ mod = find_module(obj->name); /* * Do not mess work of klp_module_coming() and klp_module_going(). * Note that the patch might still be needed before klp_module_going() * is called. Module functions can be called even in the GOING state * until mod->exit() finishes. This is especially important for * patches that modify semantic of the functions. */ if (mod && mod->klp_alive) obj->mod = mod; rcu_read_unlock_sched();} static bool klp_initialized(void){ return !!klp_root_kobj;} static struct klp_func *klp_find_func(struct klp_object *obj, struct klp_func *old_func){ struct klp_func *func; klp_for_each_func(obj, func) { if ((strcmp(old_func->old_name, func->old_name) == 0) && (old_func->old_sympos == func->old_sympos)) { return func; } } return NULL;} static struct klp_object *klp_find_object(struct klp_patch *patch, struct klp_object *old_obj){ struct klp_object *obj; klp_for_each_object(patch, obj) { if (klp_is_module(old_obj)) { if (klp_is_module(obj) && strcmp(old_obj->name, obj->name) == 0) { return obj; } } else if (!klp_is_module(obj)) { return obj; } } return NULL;} struct klp_find_arg { const char *name; unsigned long addr; unsigned long count; unsigned long pos;}; static int klp_match_callback(void *data, unsigned long addr){ struct klp_find_arg *args = data; args->addr = addr; args->count++; /* * Finish the search when the symbol is found for the desired position * or the position is not defined for a non-unique symbol. */ if ((args->pos && (args->count == args->pos)) || (!args->pos && (args->count > 1))) return 1; return 0;} static int klp_find_callback(void *data, const char *name, unsigned long addr){ struct klp_find_arg *args = data; if (strcmp(args->name, name)) return 0; return klp_match_callback(data, addr);} static int klp_find_object_symbol(const char *objname, const char *name, unsigned long sympos, unsigned long *addr){ struct klp_find_arg args = { .name = name, .addr = 0, .count = 0, .pos = sympos, }; if (objname) module_kallsyms_on_each_symbol(objname, klp_find_callback, &args); else kallsyms_on_each_match_symbol(klp_match_callback, name, &args); /* * Ensure an address was found. If sympos is 0, ensure symbol is unique; * otherwise ensure the symbol position count matches sympos. */ if (args.addr == 0) pr_err("symbol '%s' not found in symbol table\n", name); else if (args.count > 1 && sympos == 0) { pr_err("unresolvable ambiguity for symbol '%s' in object '%s'\n", name, objname); } else if (sympos != args.count && sympos > 0) { pr_err("symbol position %lu for symbol '%s' in object '%s' not found\n", sympos, name, objname ? objname : "vmlinux"); } else { *addr = args.addr; return 0; } *addr = 0; return -EINVAL;} static int klp_resolve_symbols(Elf_Shdr *sechdrs, const char *strtab, unsigned int symndx, Elf_Shdr *relasec, const char *sec_objname){ int i, cnt, ret; char sym_objname[MODULE_NAME_LEN]; char sym_name[KSYM_NAME_LEN]; Elf_Rela *relas; Elf_Sym *sym; unsigned long sympos, addr; bool sym_vmlinux; bool sec_vmlinux = !strcmp(sec_objname, "vmlinux"); /* * Since the field widths for sym_objname and sym_name in the sscanf() * call are hard-coded and correspond to MODULE_NAME_LEN and * KSYM_NAME_LEN respectively, we must make sure that MODULE_NAME_LEN * and KSYM_NAME_LEN have the values we expect them to have. * * Because the value of MODULE_NAME_LEN can differ among architectures, * we use the smallest/strictest upper bound possible (56, based on * the current definition of MODULE_NAME_LEN) to prevent overflows. */ BUILD_BUG_ON(MODULE_NAME_LEN < 56 || KSYM_NAME_LEN != 512); relas = (Elf_Rela *) relasec->sh_addr; /* For each rela in this klp relocation section */ for (i = 0; i < relasec->sh_size / sizeof(Elf_Rela); i++) { sym = (Elf_Sym *)sechdrs[symndx].sh_addr + ELF_R_SYM(relas[i].r_info); if (sym->st_shndx != SHN_LIVEPATCH) { pr_err("symbol %s is not marked as a livepatch symbol\n", strtab + sym->st_name); return -EINVAL; } /* Format: .klp.sym.sym_objname.sym_name,sympos */ cnt = sscanf(strtab + sym->st_name, ".klp.sym.%55[^.].%511[^,],%lu", sym_objname, sym_name, &sympos); if (cnt != 3) { pr_err("symbol %s has an incorrectly formatted name\n", strtab + sym->st_name); return -EINVAL; } sym_vmlinux = !strcmp(sym_objname, "vmlinux"); /* * Prevent module-specific KLP rela sections from referencing * vmlinux symbols. This helps prevent ordering issues with * module special section initializations. Presumably such * symbols are exported and normal relas can be used instead. */ if (!sec_vmlinux && sym_vmlinux) { pr_err("invalid access to vmlinux symbol '%s' from module-specific livepatch relocation section\n", sym_name); return -EINVAL; } /* klp_find_object_symbol() treats a NULL objname as vmlinux */ ret = klp_find_object_symbol(sym_vmlinux ? NULL : sym_objname, sym_name, sympos, &addr); if (ret) return ret; sym->st_value = addr; } return 0;} void __weak clear_relocate_add(Elf_Shdr *sechdrs, const char *strtab, unsigned int symindex, unsigned int relsec, struct module *me){} /* * At a high-level, there are two types of klp relocation sections: those which * reference symbols which live in vmlinux; and those which reference symbols * which live in other modules. This function is called for both types: * * 1) When a klp module itself loads, the module code calls this function to * write vmlinux-specific klp relocations (.klp.rela.vmlinux.* sections). * These relocations are written to the klp module text to allow the patched * code/data to reference unexported vmlinux symbols. They're written as * early as possible to ensure that other module init code (.e.g., * jump_label_apply_nops) can access any unexported vmlinux symbols which * might be referenced by the klp module's special sections. * * 2) When a to-be-patched module loads -- or is already loaded when a * corresponding klp module loads -- klp code calls this function to write * module-specific klp relocations (.klp.rela.{module}.* sections). These * are written to the klp module text to allow the patched code/data to * reference symbols which live in the to-be-patched module or one of its * module dependencies. Exported symbols are supported, in addition to * unexported symbols, in order to enable late module patching, which allows * the to-be-patched module to be loaded and patched sometime *after* the * klp module is loaded. */static int klp_write_section_relocs(struct module *pmod, Elf_Shdr *sechdrs, const char *shstrtab, const char *strtab, unsigned int symndx, unsigned int secndx, const char *objname, bool apply){ int cnt, ret; char sec_objname[MODULE_NAME_LEN]; Elf_Shdr *sec = sechdrs + secndx; /* * Format: .klp.rela.sec_objname.section_name * See comment in klp_resolve_symbols() for an explanation * of the selected field width value. */ cnt = sscanf(shstrtab + sec->sh_name, ".klp.rela.%55[^.]", sec_objname); if (cnt != 1) { pr_err("section %s has an incorrectly formatted name\n", shstrtab + sec->sh_name); return -EINVAL; } if (strcmp(objname ? objname : "vmlinux", sec_objname)) return 0; if (apply) { ret = klp_resolve_symbols(sechdrs, strtab, symndx, sec, sec_objname); if (ret) return ret; return apply_relocate_add(sechdrs, strtab, symndx, secndx, pmod); } clear_relocate_add(sechdrs, strtab, symndx, secndx, pmod); return 0;} int klp_apply_section_relocs(struct module *pmod, Elf_Shdr *sechdrs, const char *shstrtab, const char *strtab, unsigned int symndx, unsigned int secndx, const char *objname){ return klp_write_section_relocs(pmod, sechdrs, shstrtab, strtab, symndx, secndx, objname, true);} /* * Sysfs Interface * * /sys/kernel/livepatch * /sys/kernel/livepatch/<patch> * /sys/kernel/livepatch/<patch>/enabled * /sys/kernel/livepatch/<patch>/transition * /sys/kernel/livepatch/<patch>/force * /sys/kernel/livepatch/<patch>/replace * /sys/kernel/livepatch/<patch>/stack_order * /sys/kernel/livepatch/<patch>/<object> * /sys/kernel/livepatch/<patch>/<object>/patched * /sys/kernel/livepatch/<patch>/<object>/<function,sympos> */static int __klp_disable_patch(struct klp_patch *patch); static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count){ struct klp_patch *patch; int ret; bool enabled; ret = kstrtobool(buf, &enabled); if (ret) return ret; patch = container_of(kobj, struct klp_patch, kobj); mutex_lock(&klp_mutex); if (patch->enabled == enabled) { /* already in requested state */ ret = -EINVAL; goto out; } /* * Allow to reverse a pending transition in both ways. It might be * necessary to complete the transition without forcing and breaking * the system integrity. * * Do not allow to re-enable a disabled patch. */ if (patch == klp_transition_patch) klp_reverse_transition(); else if (!enabled) ret = __klp_disable_patch(patch); else ret = -EINVAL; out: mutex_unlock(&klp_mutex); if (ret) return ret; return count;} static ssize_t enabled_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ struct klp_patch *patch; patch = container_of(kobj, struct klp_patch, kobj); return sysfs_emit(buf, "%d\n", patch->enabled);} static ssize_t transition_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ struct klp_patch *patch; patch = container_of(kobj, struct klp_patch, kobj); return sysfs_emit(buf, "%d\n", patch == klp_transition_patch);} static ssize_t force_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count){ struct klp_patch *patch; int ret; bool val; ret = kstrtobool(buf, &val); if (ret) return ret; if (!val) return count; mutex_lock(&klp_mutex); patch = container_of(kobj, struct klp_patch, kobj); if (patch != klp_transition_patch) { mutex_unlock(&klp_mutex); return -EINVAL; } klp_force_transition(); mutex_unlock(&klp_mutex); return count;} static ssize_t replace_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ struct klp_patch *patch; patch = container_of(kobj, struct klp_patch, kobj); return sysfs_emit(buf, "%d\n", patch->replace);} static ssize_t stack_order_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ struct klp_patch *patch, *this_patch; int stack_order = 0; this_patch = container_of(kobj, struct klp_patch, kobj); mutex_lock(&klp_mutex); klp_for_each_patch(patch) { stack_order++; if (patch == this_patch) break; } mutex_unlock(&klp_mutex); return sysfs_emit(buf, "%d\n", stack_order);} static struct kobj_attribute enabled_kobj_attr = __ATTR_RW(enabled);static struct kobj_attribute transition_kobj_attr = __ATTR_RO(transition);static struct kobj_attribute force_kobj_attr = __ATTR_WO(force);static struct kobj_attribute replace_kobj_attr = __ATTR_RO(replace);static struct kobj_attribute stack_order_kobj_attr = __ATTR_RO(stack_order);static struct attribute *klp_patch_attrs[] = { &enabled_kobj_attr.attr, &transition_kobj_attr.attr, &force_kobj_attr.attr, &replace_kobj_attr.attr, &stack_order_kobj_attr.attr, NULL};ATTRIBUTE_GROUPS(klp_patch); static ssize_t patched_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ struct klp_object *obj; obj = container_of(kobj, struct klp_object, kobj); return sysfs_emit(buf, "%d\n", obj->patched);} Lines 1–500 of 1361. Load more
// SPDX-License-Identifier: GPL-2.0-or-later /* * core.c - Kernel Live Patching Core * * Copyright (C) 2014 Seth Jennings <sjenning@redhat.com> * Copyright (C) 2014 SUSE */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> #include <linux/kernel.h> #include <linux/mutex.h> #include <linux/slab.h> #include <linux/list.h> #include <linux/kallsyms.h> #include <linux/livepatch.h> #include <linux/elf.h> #include <linux/moduleloader.h> #include <linux/completion.h> #include <linux/memory.h> #include <linux/rcupdate.h> #include <asm/cacheflush.h> #include "core.h" #include "patch.h" #include "state.h" #include "transition.h" /* * klp_mutex is a coarse lock which serializes access to klp data. All * accesses to klp-related variables and structures must have mutex protection, * except within the following functions which carefully avoid the need for it: * * - klp_ftrace_handler() * - klp_update_patch_state() * - __klp_sched_try_switch() */ DEFINE_MUTEX(klp_mutex); /* * Actively used patches: enabled or in transition. Note that replaced * or disabled patches are not listed even though the related kernel * module still can be loaded. */ LIST_HEAD(klp_patches); static struct kobject *klp_root_kobj; static bool klp_is_module(struct klp_object *obj) { return obj->name; } /* sets obj->mod if object is not vmlinux and module is found */ static void klp_find_object_module(struct klp_object *obj) { struct module *mod; if (!klp_is_module(obj)) return; rcu_read_lock_sched(); /* * We do not want to block removal of patched modules and therefore * we do not take a reference here. The patches are removed by * klp_module_going() instead. */ mod = find_module(obj->name); /* * Do not mess work of klp_module_coming() and klp_module_going(). * Note that the patch might still be needed before klp_module_going() * is called. Module functions can be called even in the GOING state * until mod->exit() finishes. This is especially important for * patches that modify semantic of the functions. */ if (mod && mod->klp_alive) obj->mod = mod; rcu_read_unlock_sched(); } static bool klp_initialized(void) { return !!klp_root_kobj; } static struct klp_func *klp_find_func(struct klp_object *obj, struct klp_func *old_func) { struct klp_func *func; klp_for_each_func(obj, func) { if ((strcmp(old_func->old_name, func->old_name) == 0) && (old_func->old_sympos == func->old_sympos)) { return func; } } return NULL; } static struct klp_object *klp_find_object(struct klp_patch *patch, struct klp_object *old_obj) { struct klp_object *obj; klp_for_each_object(patch, obj) { if (klp_is_module(old_obj)) { if (klp_is_module(obj) && strcmp(old_obj->name, obj->name) == 0) { return obj; } } else if (!klp_is_module(obj)) { return obj; } } return NULL; } struct klp_find_arg { const char *name; unsigned long addr; unsigned long count; unsigned long pos; }; static int klp_match_callback(void *data, unsigned long addr) { struct klp_find_arg *args = data; args->addr = addr; args->count++; /* * Finish the search when the symbol is found for the desired position * or the position is not defined for a non-unique symbol. */ if ((args->pos && (args->count == args->pos)) || (!args->pos && (args->count > 1))) return 1; return 0; } static int klp_find_callback(void *data, const char *name, unsigned long addr) { struct klp_find_arg *args = data; if (strcmp(args->name, name)) return 0; return klp_match_callback(data, addr); } static int klp_find_object_symbol(const char *objname, const char *name, unsigned long sympos, unsigned long *addr) { struct klp_find_arg args = { .name = name, .addr = 0, .count = 0, .pos = sympos, }; if (objname) module_kallsyms_on_each_symbol(objname, klp_find_callback, &args); else kallsyms_on_each_match_symbol(klp_match_callback, name, &args); /* * Ensure an address was found. If sympos is 0, ensure symbol is unique; * otherwise ensure the symbol position count matches sympos. */ if (args.addr == 0) pr_err("symbol '%s' not found in symbol table\n", name); else if (args.count > 1 && sympos == 0) { pr_err("unresolvable ambiguity for symbol '%s' in object '%s'\n", name, objname); } else if (sympos != args.count && sympos > 0) { pr_err("symbol position %lu for symbol '%s' in object '%s' not found\n", sympos, name, objname ? objname : "vmlinux"); } else { *addr = args.addr; return 0; } *addr = 0; return -EINVAL; } static int klp_resolve_symbols(Elf_Shdr *sechdrs, const char *strtab, unsigned int symndx, Elf_Shdr *relasec, const char *sec_objname) { int i, cnt, ret; char sym_objname[MODULE_NAME_LEN]; char sym_name[KSYM_NAME_LEN]; Elf_Rela *relas; Elf_Sym *sym; unsigned long sympos, addr; bool sym_vmlinux; bool sec_vmlinux = !strcmp(sec_objname, "vmlinux"); /* * Since the field widths for sym_objname and sym_name in the sscanf() * call are hard-coded and correspond to MODULE_NAME_LEN and * KSYM_NAME_LEN respectively, we must make sure that MODULE_NAME_LEN * and KSYM_NAME_LEN have the values we expect them to have. * * Because the value of MODULE_NAME_LEN can differ among architectures, * we use the smallest/strictest upper bound possible (56, based on * the current definition of MODULE_NAME_LEN) to prevent overflows. */ BUILD_BUG_ON(MODULE_NAME_LEN < 56 || KSYM_NAME_LEN != 512); relas = (Elf_Rela *) relasec->sh_addr; /* For each rela in this klp relocation section */ for (i = 0; i < relasec->sh_size / sizeof(Elf_Rela); i++) { sym = (Elf_Sym *)sechdrs[symndx].sh_addr + ELF_R_SYM(relas[i].r_info); if (sym->st_shndx != SHN_LIVEPATCH) { pr_err("symbol %s is not marked as a livepatch symbol\n", strtab + sym->st_name); return -EINVAL; } /* Format: .klp.sym.sym_objname.sym_name,sympos */ cnt = sscanf(strtab + sym->st_name, ".klp.sym.%55[^.].%511[^,],%lu", sym_objname, sym_name, &sympos); if (cnt != 3) { pr_err("symbol %s has an incorrectly formatted name\n", strtab + sym->st_name); return -EINVAL; } sym_vmlinux = !strcmp(sym_objname, "vmlinux"); /* * Prevent module-specific KLP rela sections from referencing * vmlinux symbols. This helps prevent ordering issues with * module special section initializations. Presumably such * symbols are exported and normal relas can be used instead. */ if (!sec_vmlinux && sym_vmlinux) { pr_err("invalid access to vmlinux symbol '%s' from module-specific livepatch relocation section\n", sym_name); return -EINVAL; } /* klp_find_object_symbol() treats a NULL objname as vmlinux */ ret = klp_find_object_symbol(sym_vmlinux ? NULL : sym_objname, sym_name, sympos, &addr); if (ret) return ret; sym->st_value = addr; } return 0; } void __weak clear_relocate_add(Elf_Shdr *sechdrs, const char *strtab, unsigned int symindex, unsigned int relsec, struct module *me) { } /* * At a high-level, there are two types of klp relocation sections: those which * reference symbols which live in vmlinux; and those which reference symbols * which live in other modules. This function is called for both types: * * 1) When a klp module itself loads, the module code calls this function to * write vmlinux-specific klp relocations (.klp.rela.vmlinux.* sections). * These relocations are written to the klp module text to allow the patched * code/data to reference unexported vmlinux symbols. They're written as * early as possible to ensure that other module init code (.e.g., * jump_label_apply_nops) can access any unexported vmlinux symbols which * might be referenced by the klp module's special sections. * * 2) When a to-be-patched module loads -- or is already loaded when a * corresponding klp module loads -- klp code calls this function to write * module-specific klp relocations (.klp.rela.{module}.* sections). These * are written to the klp module text to allow the patched code/data to * reference symbols which live in the to-be-patched module or one of its * module dependencies. Exported symbols are supported, in addition to * unexported symbols, in order to enable late module patching, which allows * the to-be-patched module to be loaded and patched sometime *after* the * klp module is loaded. */ static int klp_write_section_relocs(struct module *pmod, Elf_Shdr *sechdrs, const char *shstrtab, const char *strtab, unsigned int symndx, unsigned int secndx, const char *objname, bool apply) { int cnt, ret; char sec_objname[MODULE_NAME_LEN]; Elf_Shdr *sec = sechdrs + secndx; /* * Format: .klp.rela.sec_objname.section_name * See comment in klp_resolve_symbols() for an explanation * of the selected field width value. */ cnt = sscanf(shstrtab + sec->sh_name, ".klp.rela.%55[^.]", sec_objname); if (cnt != 1) { pr_err("section %s has an incorrectly formatted name\n", shstrtab + sec->sh_name); return -EINVAL; } if (strcmp(objname ? objname : "vmlinux", sec_objname)) return 0; if (apply) { ret = klp_resolve_symbols(sechdrs, strtab, symndx, sec, sec_objname); if (ret) return ret; return apply_relocate_add(sechdrs, strtab, symndx, secndx, pmod); } clear_relocate_add(sechdrs, strtab, symndx, secndx, pmod); return 0; } int klp_apply_section_relocs(struct module *pmod, Elf_Shdr *sechdrs, const char *shstrtab, const char *strtab, unsigned int symndx, unsigned int secndx, const char *objname) { return klp_write_section_relocs(pmod, sechdrs, shstrtab, strtab, symndx, secndx, objname, true); } /* * Sysfs Interface * * /sys/kernel/livepatch * /sys/kernel/livepatch/<patch> * /sys/kernel/livepatch/<patch>/enabled * /sys/kernel/livepatch/<patch>/transition * /sys/kernel/livepatch/<patch>/force * /sys/kernel/livepatch/<patch>/replace * /sys/kernel/livepatch/<patch>/stack_order * /sys/kernel/livepatch/<patch>/<object> * /sys/kernel/livepatch/<patch>/<object>/patched * /sys/kernel/livepatch/<patch>/<object>/<function,sympos> */ static int __klp_disable_patch(struct klp_patch *patch); static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { struct klp_patch *patch; int ret; bool enabled; ret = kstrtobool(buf, &enabled); if (ret) return ret; patch = container_of(kobj, struct klp_patch, kobj); mutex_lock(&klp_mutex); if (patch->enabled == enabled) { /* already in requested state */ ret = -EINVAL; goto out; } /* * Allow to reverse a pending transition in both ways. It might be * necessary to complete the transition without forcing and breaking * the system integrity. * * Do not allow to re-enable a disabled patch. */ if (patch == klp_transition_patch) klp_reverse_transition(); else if (!enabled) ret = __klp_disable_patch(patch); else ret = -EINVAL; out: mutex_unlock(&klp_mutex); if (ret) return ret; return count; } static ssize_t enabled_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct klp_patch *patch; patch = container_of(kobj, struct klp_patch, kobj); return sysfs_emit(buf, "%d\n", patch->enabled); } static ssize_t transition_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct klp_patch *patch; patch = container_of(kobj, struct klp_patch, kobj); return sysfs_emit(buf, "%d\n", patch == klp_transition_patch); } static ssize_t force_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { struct klp_patch *patch; int ret; bool val; ret = kstrtobool(buf, &val); if (ret) return ret; if (!val) return count; mutex_lock(&klp_mutex); patch = container_of(kobj, struct klp_patch, kobj); if (patch != klp_transition_patch) { mutex_unlock(&klp_mutex); return -EINVAL; } klp_force_transition(); mutex_unlock(&klp_mutex); return count; } static ssize_t replace_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct klp_patch *patch; patch = container_of(kobj, struct klp_patch, kobj); return sysfs_emit(buf, "%d\n", patch->replace); } static ssize_t stack_order_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct klp_patch *patch, *this_patch; int stack_order = 0; this_patch = container_of(kobj, struct klp_patch, kobj); mutex_lock(&klp_mutex); klp_for_each_patch(patch) { stack_order++; if (patch == this_patch) break; } mutex_unlock(&klp_mutex); return sysfs_emit(buf, "%d\n", stack_order); } static struct kobj_attribute enabled_kobj_attr = __ATTR_RW(enabled); static struct kobj_attribute transition_kobj_attr = __ATTR_RO(transition); static struct kobj_attribute force_kobj_attr = __ATTR_WO(force); static struct kobj_attribute replace_kobj_attr = __ATTR_RO(replace); static struct kobj_attribute stack_order_kobj_attr = __ATTR_RO(stack_order); static struct attribute *klp_patch_attrs[] = { &enabled_kobj_attr.attr, &transition_kobj_attr.attr, &force_kobj_attr.attr, &replace_kobj_attr.attr, &stack_order_kobj_attr.attr, NULL }; ATTRIBUTE_GROUPS(klp_patch); static ssize_t patched_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct klp_object *obj; obj = container_of(kobj, struct klp_object, kobj); return sysfs_emit(buf, "%d\n", obj->patched); }